Преглед на файлове

二手相关表映射导入

zhangchen преди 3 месеца
родител
ревизия
8b7767dbc9
променени са 100 файла, в които са добавени 14232 реда и са изтрити 17 реда
  1. 24 17
      alien-branch-second/pom.xml
  2. 33 0
      alien-branch-second/src/main/java/shop/alien/second/AlienSecondApplication.java
  3. 471 0
      alien-branch-second/src/main/java/shop/alien/second/config/GaoDeMapUtil.java
  4. 57 0
      alien-branch-second/src/main/java/shop/alien/second/config/SwaggerConfig.java
  5. 69 0
      alien-branch-second/src/main/java/shop/alien/second/controller/AdminSecondGoodsController.java
  6. 40 0
      alien-branch-second/src/main/java/shop/alien/second/controller/RiskControlController.java
  7. 69 0
      alien-branch-second/src/main/java/shop/alien/second/controller/RiskControlGoodsController.java
  8. 84 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SearchGoodsController.java
  9. 206 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SecondEntrustUserController.java
  10. 33 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SecondGaoDeController.java
  11. 33 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SecondGoodsCategoryController.java
  12. 361 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SecondGoodsController.java
  13. 43 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SecondGoodsReportingController.java
  14. 78 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SecondRecommendController.java
  15. 72 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SecondShieldController.java
  16. 221 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SecondTradeRecordController.java
  17. 97 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SecondUserCreditController.java
  18. 89 0
      alien-branch-second/src/main/java/shop/alien/second/controller/SecondUserCreditRecordController.java
  19. 47 0
      alien-branch-second/src/main/java/shop/alien/second/controller/TestController.java
  20. 226 0
      alien-branch-second/src/main/java/shop/alien/second/controller/UserGoodsController.java
  21. 77 0
      alien-branch-second/src/main/java/shop/alien/second/controller/VideoModerationController.java
  22. 39 0
      alien-branch-second/src/main/java/shop/alien/second/exception/GlobalExceptionHandler.java
  23. 40 0
      alien-branch-second/src/main/java/shop/alien/second/feign/AlienStoreFeign.java
  24. 62 0
      alien-branch-second/src/main/java/shop/alien/second/platform/PlatformRiskControlController.java
  25. 70 0
      alien-branch-second/src/main/java/shop/alien/second/platform/PlatformSecondCategoryController.java
  26. 85 0
      alien-branch-second/src/main/java/shop/alien/second/platform/PlatformSecondTradeController.java
  27. 39 0
      alien-branch-second/src/main/java/shop/alien/second/platform/PlatformSecondUserFreezeController.java
  28. 57 0
      alien-branch-second/src/main/java/shop/alien/second/platform/PlatformUserViolationController.java
  29. 30 0
      alien-branch-second/src/main/java/shop/alien/second/service/PlatformSecondTradeService.java
  30. 16 0
      alien-branch-second/src/main/java/shop/alien/second/service/PlatformUserViolationService.java
  31. 26 0
      alien-branch-second/src/main/java/shop/alien/second/service/RiskControlGoodsService.java
  32. 63 0
      alien-branch-second/src/main/java/shop/alien/second/service/RiskControlService.java
  33. 90 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondEntrustUserService.java
  34. 98 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsAuditService.java
  35. 26 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsCategoryService.java
  36. 29 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsNotificationService.java
  37. 18 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsOperationRecordService.java
  38. 25 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsReportingService.java
  39. 226 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsService.java
  40. 48 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondRecommendService.java
  41. 61 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondTradeRecordService.java
  42. 41 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondUserCreditRecordService.java
  43. 38 0
      alien-branch-second/src/main/java/shop/alien/second/service/SecondUserCreditService.java
  44. 41 0
      alien-branch-second/src/main/java/shop/alien/second/service/VideoModerationService.java
  45. 385 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/PlatformSecondTradeServiceImpl.java
  46. 122 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/PlatformUserViolationServiceImpl.java
  47. 317 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/RiskControlGoodsServiceImpl.java
  48. 267 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/RiskControlServiceImpl.java
  49. 296 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondEntrustUserServiceImpl.java
  50. 769 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsAuditServiceImpl.java
  51. 153 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsCategoryServiceImpl.java
  52. 151 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsNotificationServiceImpl.java
  53. 127 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsOperationRecordServiceImpl.java
  54. 301 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsReportingServiceImpl.java
  55. 2288 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsServiceImpl.java
  56. 292 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondRecommendServiceImpl.java
  57. 1235 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondTradeRecordServiceImpl.java
  58. 249 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondUserCreditRecordServiceImpl.java
  59. 219 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondUserCreditServiceImpl.java
  60. 275 0
      alien-branch-second/src/main/java/shop/alien/second/service/impl/VideoModerationServiceImpl.java
  61. 382 0
      alien-branch-second/src/main/java/shop/alien/second/task/Task.java
  62. 153 0
      alien-branch-second/src/main/java/shop/alien/second/util/AiTaskUtils.java
  63. 272 0
      alien-branch-second/src/main/java/shop/alien/second/util/AiUserViolationUtils.java
  64. 91 0
      alien-branch-second/src/main/java/shop/alien/second/util/JsonUtils.java
  65. 20 0
      alien-branch-second/src/main/resources/bootstrap-dev.yml
  66. 22 0
      alien-branch-second/src/main/resources/bootstrap-prod.yml
  67. 22 0
      alien-branch-second/src/main/resources/bootstrap-test.yml
  68. 22 0
      alien-branch-second/src/main/resources/bootstrap-uat.yml
  69. 3 0
      alien-branch-second/src/main/resources/bootstrap.yml
  70. 173 0
      alien-branch-second/src/main/resources/logback-spring.xml
  71. 66 0
      alien-service-config/pom.xml
  72. 66 0
      alien-service-config/src/main/java/shop/alien/config/databases/DruidConfig.java
  73. 63 0
      alien-service-config/src/main/java/shop/alien/config/databases/MyBatisFieldHandler.java
  74. 16 0
      alien-service-config/src/main/java/shop/alien/config/databases/MyBatisPlusPageConfig.java
  75. 64 0
      alien-service-config/src/main/java/shop/alien/config/feign/FeignBodyLogger.java
  76. 26 0
      alien-service-config/src/main/java/shop/alien/config/feign/FeignNacosConfig.java
  77. 58 0
      alien-service-config/src/main/java/shop/alien/config/feign/FeignOptionConfig.java
  78. 75 0
      alien-service-config/src/main/java/shop/alien/config/feign/GzipResponseDecoder.java
  79. 109 0
      alien-service-config/src/main/java/shop/alien/config/feign/OkHttpConfig.java
  80. 34 0
      alien-service-config/src/main/java/shop/alien/config/filter/FilterConfig.java
  81. 23 0
      alien-service-config/src/main/java/shop/alien/config/filter/HttpRequestConfig.java
  82. 38 0
      alien-service-config/src/main/java/shop/alien/config/filter/NoRepeatSubmit.java
  83. 25 0
      alien-service-config/src/main/java/shop/alien/config/filter/NoRepeatSubmitConfig.java
  84. 117 0
      alien-service-config/src/main/java/shop/alien/config/filter/NoRepeatSubmitInterceptor.java
  85. 21 0
      alien-service-config/src/main/java/shop/alien/config/filter/ReplaceStreamFilter.java
  86. 75 0
      alien-service-config/src/main/java/shop/alien/config/filter/RequestInterceptor.java
  87. 105 0
      alien-service-config/src/main/java/shop/alien/config/filter/RequestWrapper.java
  88. 39 0
      alien-service-config/src/main/java/shop/alien/config/http/HttpConfig.java
  89. 120 0
      alien-service-config/src/main/java/shop/alien/config/properties/RiskControlProperties.java
  90. 24 0
      alien-service-config/src/main/java/shop/alien/config/properties/VideoModerationProperties.java
  91. 200 0
      alien-service-config/src/main/java/shop/alien/config/redis/BaseRedisService.java
  92. 73 0
      alien-service-entity/src/main/java/shop/alien/entity/store/EssentialCityCode.java
  93. 70 0
      alien-service-entity/src/main/java/shop/alien/entity/store/LifeNotice.java
  94. 54 0
      alien-service-entity/src/main/java/shop/alien/entity/store/LifeUserLearningRecord.java
  95. 62 0
      alien-service-entity/src/main/java/shop/alien/entity/store/LifeUserLearningVideo.java
  96. 66 0
      alien-service-entity/src/main/java/shop/alien/entity/store/SecondAiTask.java
  97. 120 0
      alien-service-entity/src/main/java/shop/alien/entity/store/dto/LifeUserViolationDto.java
  98. 59 0
      alien-service-entity/src/main/java/shop/alien/entity/store/vo/LifeFansVo.java
  99. 19 0
      alien-service-entity/src/main/java/shop/alien/entity/store/vo/LifeUserLearningVideoVo.java
  100. 41 0
      alien-service-entity/src/main/java/shop/alien/entity/store/vo/LifeUserViolationVo.java

+ 24 - 17
alien-branch-second/pom.xml

@@ -17,6 +17,8 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
 
+
+
     <dependencies>
 
         <dependency>
@@ -248,23 +250,28 @@
         </dependency>
         <!-- openfeign -->
 
-<!--        <dependency>-->
-<!--            <groupId>shop.alien</groupId>-->
-<!--            <artifactId>alien-entity</artifactId>-->
-<!--            <version>1.0.0</version>-->
-<!--        </dependency>-->
-
-<!--        <dependency>-->
-<!--            <groupId>shop.alien</groupId>-->
-<!--            <artifactId>alien-config</artifactId>-->
-<!--            <version>1.0.0</version>-->
-<!--        </dependency>-->
-
-<!--        <dependency>-->
-<!--            <groupId>shop.alien</groupId>-->
-<!--            <artifactId>alien-util</artifactId>-->
-<!--            <version>1.0.0</version>-->
-<!--        </dependency>-->
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-service-config</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-service-entity</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-service-util</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
 
     </dependencies>
 

+ 33 - 0
alien-branch-second/src/main/java/shop/alien/second/AlienSecondApplication.java

@@ -0,0 +1,33 @@
+package shop.alien.second;
+
+import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+//@ComponentScan("shop.alien.second.*")
+@EnableFeignClients(basePackages = {"shop.alien.second.feign"})
+@ComponentScan({
+        "shop.alien.second.*",
+        "shop.alien.util.*",
+        "shop.alien.config.http",
+        "shop.alien.config.databases",
+        "shop.alien.config.feign",
+        "shop.alien.config.properties",
+        "shop.alien.config.redis"})
+@MapperScan({
+        "shop.alien.mapper",
+        "shop.alien.mapper.second"})
+@EnableSwaggerBootstrapUI
+@SpringBootApplication
+@EnableScheduling
+public class AlienSecondApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(AlienSecondApplication.class, args);
+    }
+
+}

+ 471 - 0
alien-branch-second/src/main/java/shop/alien/second/config/GaoDeMapUtil.java

@@ -0,0 +1,471 @@
+package shop.alien.second.config;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.store.EssentialCityCode;
+import shop.alien.mapper.EssentialCityCodeMapper;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 高德地图工具类
+ *
+ * @author ssk
+ * @version 1.0.0
+ * @discription 打死你个龟孙
+ * @since 1937-7-7
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class GaoDeMapUtil {
+
+    @Value("${gaode.key}")
+    private String key;
+
+    @Value("${gaode.geoUrl}")
+    private String geoUrl;
+
+    @Value("${gaode.geoListUrl}")
+    private String geoListUrl;
+
+    @Value("${gaode.getDistrict}")
+    private String getDistrict;
+
+    @Value("${gaode.distanceUrl}")
+    private String distanceUrl;
+
+    @Value("${gaode.distanceTypeUrl}")
+    private String distanceTypeUrl;
+
+    @Value("${gaode.nearUrl}")
+    private String nearUrl;
+
+    @Value("${gaode.subwayUrl}")
+    private String subwayUrl;
+
+    @Value("${gaode.addressUrl}")
+    private String addressUrl;
+
+    //private final EssentialCityCodeMapper essentialCityCodeMapper;
+
+    /**
+     * 地址转经纬度
+     *
+     * @param address 地址
+     * @return 经纬度
+     */
+    public String getLonAndLatByAddress(String address) {
+        String formattedUrl = String.format(geoUrl, address, key);
+        JSONObject obj = getResponse(formattedUrl);
+        if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+            JSONArray geocodesArray = obj.getJSONArray("geocodes");
+            if (geocodesArray != null && !geocodesArray.isEmpty()) {
+                JSONObject jobJSON = geocodesArray.getJSONObject(0);
+                return jobJSON.getString("location");
+            } else {
+                log.error("AddressLocationUtil.getLonAndLatByAddress ERROR 未找到与地址匹配的经纬度信息");
+                return "ERROR 未找到与地址匹配的经纬度信息";
+            }
+        } else {
+            log.error("AddressLocationUtil.getLonAndLatByAddress ERROR 地址转换经纬度失败");
+            return "ERROR 地址转换经纬度失败";
+        }
+    }
+
+    /**
+     * 地址转经纬度
+     *
+     * @param address 地址
+     * @return 经纬度
+     */
+    public JSONObject getInputPrompt(String address, String city) {
+        //如果按照城市查询
+        String cityCode = "";
+        if (StringUtils.isNotEmpty(city)) {
+//            EssentialCityCode essentialCityCode = essentialCityCodeMapper.selectOne(new LambdaQueryWrapper<EssentialCityCode>().eq(EssentialCityCode::getAreaName, city));
+//            if (null != essentialCityCode) {
+//                cityCode = essentialCityCode.getCityCode().toString();
+//            }
+        }
+        String formattedUrl = String.format(geoListUrl, address, key, cityCode);
+        JSONObject obj = getResponse(formattedUrl);
+        return obj;
+    }
+
+    /**
+     * 地址转经纬度
+     * cityCode:城市编码
+     *
+     * @return
+     */
+    public JSONObject getDistrict(String cityCode) {
+        String formattedUrl = String.format(getDistrict, key, cityCode);
+        JSONObject obj = getResponse(formattedUrl);
+        return obj;
+    }
+
+    /**
+     * 计算两个经纬度的距离
+     *
+     * @param longitudeOne 经度1
+     * @param latitudeOne  纬度1
+     * @param longitudeTwo 经度2
+     * @param latitudeTwo  纬度2
+     * @return distance
+     */
+    public Double getDistance(String longitudeOne, String latitudeOne, String longitudeTwo, String latitudeTwo) {
+        try {
+            String urlString = String.format(distanceUrl, key, longitudeOne, latitudeOne, longitudeTwo, latitudeTwo);
+            JSONObject obj = getResponse(urlString);
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                JSONArray resultsArray = obj.getJSONArray("results");
+                if (resultsArray != null && !resultsArray.isEmpty()) {
+                    JSONObject resultJSON = resultsArray.getJSONObject(0);
+                    return Double.parseDouble(resultJSON.getString("distance"));
+                } else {
+                    log.error("AddressLocationUtil.getDistance ERROR 计算距离失败,结果为空");
+                    return null;
+                }
+            } else {
+                log.error("AddressLocationUtil.getDistance ERROR 计算距离失败");
+                return null;
+            }
+        } catch (Exception e) {
+            log.error("AddressLocationUtil.getDistance ERROR {}", e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 计算两个经纬度的距离
+     *
+     * @param longitudeOne 经度1
+     * @param latitudeOne  纬度1
+     * @param longitudeTwo 经度2
+     * @param latitudeTwo  纬度2
+     * @return distance
+     */
+    public Double getDistanceStraightLine(String longitudeOne, String latitudeOne, String longitudeTwo, String latitudeTwo) {
+        try {
+            String urlString = String.format(distanceTypeUrl, key, longitudeOne, latitudeOne, longitudeTwo, latitudeTwo, 0);
+            JSONObject obj = getResponse(urlString);
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                JSONArray resultsArray = obj.getJSONArray("results");
+                if (resultsArray != null && !resultsArray.isEmpty()) {
+                    JSONObject resultJSON = resultsArray.getJSONObject(0);
+                    return Double.parseDouble(resultJSON.getString("distance"));
+                } else {
+                    log.error("AddressLocationUtil.getDistance ERROR 计算距离失败,结果为空");
+                    return null;
+                }
+            } else {
+                log.error("AddressLocationUtil.getDistance ERROR 计算距离失败");
+                return null;
+            }
+        } catch (Exception e) {
+            log.error("AddressLocationUtil.getDistance ERROR {}", e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 获取附近商家、建筑等信息
+     *
+     * @param longitude 经度
+     * @param latitude  纬度
+     * @return
+     */
+    public JSONArray getNear(String longitude, String latitude) {
+        try {
+            String urlString = String.format(nearUrl, longitude, latitude, key, 10000, 20, 1);
+            JSONObject obj = getResponse(urlString);
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                JSONArray resultsArray = obj.getJSONArray("pois");
+                return resultsArray;
+            }
+        } catch (Exception e) {
+            log.error("AddressLocationUtil.getNear ERROR {}", e.getMessage());
+        }
+        return new JSONArray();
+    }
+
+    /**
+     * 获取10公里范围内的商场
+     *
+     * @param longitude 经度
+     * @param latitude  纬度
+     * @return 商场列表
+     */
+    public JSONArray getNearbyShoppingMalls(String longitude, String latitude) {
+        try {
+            // 使用高德地图POI搜索接口,搜索类型为商场(060100)
+            String urlString = String.format(nearUrl, longitude, latitude, key, 10000, 20, 1) + "&types=060100";
+            JSONObject obj = getResponse(urlString);
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                JSONArray resultsArray = obj.getJSONArray("pois");
+                return resultsArray;
+            }
+        } catch (Exception e) {
+            log.error("GaoDeMapUtil.getNearbyShoppingMalls ERROR {}", e.getMessage());
+        }
+        return new JSONArray();
+    }
+
+    public JSONObject getNearbySubway(String longitude, String latitude) {
+        try {
+            // 使用高德地图POI搜索接口,搜索类型为地铁站(010100)
+            String encodedKeywords = URLEncoder.encode("地铁站", "UTF-8");
+            String urlString = String.format(
+                    subwayUrl,
+                    key, latitude, longitude, encodedKeywords, 1000, 150500);
+            JSONObject obj = getResponse(urlString);
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                JSONObject pois = obj.getJSONArray("pois").getJSONObject(0);
+                return pois;
+            }
+        } catch (Exception e) {
+            log.error("GaoDeMapUtil.getNearbySubway ERROR {}", e.getMessage());
+        }
+        return new JSONObject();
+    }
+
+    /**
+     * 通过经纬度获取地址信息
+     *
+     * @param longitude
+     * @param latitude
+     * @param radius
+     * @param extensions
+     * @return
+     */
+    public Map<String, String> getAddressByLonAndLat(String longitude, String latitude, String radius, String extensions) {
+        String formattedUrl = String.format(addressUrl, longitude, latitude, key, radius, extensions);
+        JSONObject obj = getResponse(formattedUrl);
+        try {
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                Map<String, String> address = new HashMap<>();
+                JSONObject regeocodeObject = obj.getJSONObject("regeocode");
+                JSONObject addressComponent = regeocodeObject.getJSONObject("addressComponent");
+                address.put("city", addressComponent.getString("city"));
+                address.put("district", addressComponent.getString("district"));
+                address.put("province", addressComponent.getString("province"));
+                JSONObject streetNumber = addressComponent.getJSONObject("streetNumber");
+                address.put("street", streetNumber.getString("street"));
+                address.put("streetNumber", streetNumber.getString("number"));
+                JSONObject pois = regeocodeObject.getJSONArray("pois").getJSONObject(0);
+                address.put("poiName", pois.getString("name"));
+                return address;
+            }
+        } catch (Exception e) {
+            log.error("GaoDeMapUtil.getAddressByLonAndLat ERROR {}", e.getMessage());
+        }
+        return new HashMap<>();
+    }
+
+    /**
+     * 根据经纬度获取当前城市与城市半径
+     *
+     * @param longitude 经度
+     * @param latitude  纬度
+     * @return Map包含城市名称、城市编码、城市中心点、城市半径(米)
+     */
+    public Map<String, Object> getCityAndRadiusByLonAndLat(String longitude, String latitude) {
+        Map<String, Object> result = new HashMap<>();
+        try {
+            // 1. 通过逆地理编码获取城市信息
+            String formattedUrl = String.format(addressUrl, longitude, latitude, key, "1000", "base");
+            JSONObject geoObj = getResponse(formattedUrl);
+            
+            if (null == geoObj || !"1".equals(String.valueOf(geoObj.get("status")))) {
+                log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat 逆地理编码失败");
+                return result;
+            }
+            
+            JSONObject regeocodeObject = geoObj.getJSONObject("regeocode");
+            JSONObject addressComponent = regeocodeObject.getJSONObject("addressComponent");
+            
+            String cityName = addressComponent.getString("city");
+            String adcode = addressComponent.getString("adcode");  // 区县级别代码
+            String province = addressComponent.getString("province");
+            
+            // 判断是否是直辖市
+            boolean isMunicipality = StringUtils.isEmpty(cityName) || "[]".equals(cityName);
+            if (isMunicipality) {
+                cityName = province;
+            }
+            
+            // 获取城市级别的行政区划代码
+            String cityCode = getCityLevelCode(adcode, isMunicipality, province);
+            
+            result.put("city", cityName);
+            result.put("province", province);
+            result.put("cityCode", cityCode);
+            
+            // 2. 通过行政区域查询获取城市边界信息
+            // 注意:必须添加 extensions=all 和 subdistrict=0 参数才能获取到polyline边界数据
+            String districtUrl = String.format(getDistrict, key, cityCode) + "&extensions=all&subdistrict=0";
+            JSONObject districtObj = getResponse(districtUrl);
+            
+            if (null != districtObj && "1".equals(String.valueOf(districtObj.get("status")))) {
+                JSONArray districts = districtObj.getJSONArray("districts");
+                if (districts != null && !districts.isEmpty()) {
+                    JSONObject district = districts.getJSONObject(0);
+                    String center = district.getString("center");
+                    result.put("center", center);
+                    
+                    // 3. 计算城市半径:获取边界坐标,计算中心点到边界的最大距离
+                    String polyline = district.getString("polyline");
+                    if (StringUtils.isNotEmpty(polyline) && !"|".equals(polyline)) {
+                        Double radius = calculateCityRadius(center, polyline);
+                        result.put("radius", radius);
+                        // 保留两位小数
+                        result.put("radiusKm", Math.round(radius / 10.0) / 100.0);
+                    } else {
+                        // 如果没有边界数据,返回默认半径
+                        result.put("radius", 50000.0);  // 默认50公里
+                        result.put("radiusKm", 50.00);
+                        log.warn("GaoDeMapUtil.getCityAndRadiusByLonAndLat 城市{}没有边界数据,使用默认半径", cityName);
+                    }
+                } else {
+                    log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat 未查询到城市行政区域信息");
+                }
+            } else {
+                log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat 行政区域查询失败");
+            }
+            
+        } catch (Exception e) {
+            log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat ERROR {}", e.getMessage(), e);
+        }
+        return result;
+    }
+
+    /**
+     * 获取城市级别的行政区划代码
+     * 
+     * @param adcode 区县级别的行政区划代码
+     * @param isMunicipality 是否是直辖市
+     * @param province 省份名称
+     * @return 城市级别的行政区划代码
+     */
+    private String getCityLevelCode(String adcode, boolean isMunicipality, String province) {
+        if (StringUtils.isEmpty(adcode) || adcode.length() != 6) {
+            return adcode;
+        }
+        
+        // 直辖市(北京、上海、天津、重庆):使用省级代码(前2位+0000)
+        if (isMunicipality || "北京市".equals(province) || "上海市".equals(province) 
+                || "天津市".equals(province) || "重庆市".equals(province)) {
+            return adcode.substring(0, 2) + "0000";
+        }
+        
+        // 普通地级市:使用市级代码(前4位+00)
+        return adcode.substring(0, 4) + "00";
+    }
+
+    /**
+     * 计算城市半径:计算中心点到边界的最大直线距离
+     *
+     * @param center   中心点坐标 "经度,纬度"
+     * @param polyline 边界坐标串 "经度,纬度;经度,纬度;..." 或 "经度,纬度;经度,纬度|经度,纬度;经度,纬度"
+     * @return 城市半径(米)
+     */
+    private Double calculateCityRadius(String center, String polyline) {
+        try {
+            String[] centerCoords = center.split(",");
+            double centerLon = Double.parseDouble(centerCoords[0]);
+            double centerLat = Double.parseDouble(centerCoords[1]);
+            
+            // 处理多个区域的情况(用|分隔)
+            String[] areas = polyline.split("\\|");
+            double maxDistance = 0.0;
+            
+            for (String area : areas) {
+                String[] points = area.split(";");
+                // 采样边界点,避免计算量过大(每隔10个点采样一次)
+                int step = Math.max(1, points.length / 50);
+                for (int i = 0; i < points.length; i += step) {
+                    String[] pointCoords = points[i].split(",");
+                    if (pointCoords.length >= 2) {
+                        double pointLon = Double.parseDouble(pointCoords[0]);
+                        double pointLat = Double.parseDouble(pointCoords[1]);
+                        
+                        // 使用本地Haversine公式计算直线距离(不调用API,速度快)
+                        double distance = haversineDistance(centerLon, centerLat, pointLon, pointLat);
+                        if (distance > maxDistance) {
+                            maxDistance = distance;
+                        }
+                    }
+                }
+            }
+            
+            return maxDistance;
+        } catch (Exception e) {
+            log.error("GaoDeMapUtil.calculateCityRadius ERROR {}", e.getMessage());
+            return 50000.0; // 默认50公里
+        }
+    }
+
+    /**
+     * 使用Haversine公式计算两点间的直线距离(本地计算,不调用API)
+     *
+     * @param lon1 起点经度
+     * @param lat1 起点纬度
+     * @param lon2 终点经度
+     * @param lat2 终点纬度
+     * @return 距离(米)
+     */
+    private double haversineDistance(double lon1, double lat1, double lon2, double lat2) {
+        final double EARTH_RADIUS = 6371000; // 地球半径(米)
+        
+        // 转换为弧度
+        double lat1Rad = Math.toRadians(lat1);
+        double lat2Rad = Math.toRadians(lat2);
+        double deltaLat = Math.toRadians(lat2 - lat1);
+        double deltaLon = Math.toRadians(lon2 - lon1);
+        
+        // Haversine公式
+        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
+                   Math.cos(lat1Rad) * Math.cos(lat2Rad) *
+                   Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
+        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+        
+        return EARTH_RADIUS * c;
+    }
+
+    /**
+     * 创建请求
+     *
+     * @param serverUrl 请求地址
+     * @return JSONObject
+     */
+    private JSONObject getResponse(String serverUrl) {
+        StringBuilder result = new StringBuilder();
+        try {
+            URL url = new URL(serverUrl);
+            URLConnection conn = url.openConnection();
+            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+            in.close();
+        } catch (Exception e) {
+            log.error("AddressLocationUtil.getResponse ERROR {}", e.getMessage());
+            return null;
+        }
+        return JSONObject.parseObject(result.toString());
+    }
+}

+ 57 - 0
alien-branch-second/src/main/java/shop/alien/second/config/SwaggerConfig.java

@@ -0,0 +1,57 @@
+package shop.alien.second.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * swagger配置类
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4 9:17
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+    /**
+     * controller扫描
+     *
+     * @return controller扫描
+     */
+    @Bean
+    public Docket docket() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .groupName("二手平台")
+                .select()
+                .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+    /**
+     * Api标题信息
+     *
+     * @return api标题Api标题信息
+     */
+    public ApiInfo apiInfo() {
+        Contact contact = new Contact("二手平台", "https://xxxx.xxxx", "");
+        return new ApiInfoBuilder()
+                .title("二手平台")
+                .description("二手平台")
+                .license("二手平台")
+                .contact(contact)
+                .termsOfServiceUrl("https://xxxx.xxxx")
+                .version("1.0.0")
+                .build();
+    }
+}

+ 69 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/AdminSecondGoodsController.java

@@ -0,0 +1,69 @@
+package shop.alien.second.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.vo.SecondGoodsAdminQueryDTO;
+import shop.alien.entity.second.vo.SecondGoodsDetailVo;
+import shop.alien.entity.second.vo.SecondGoodsRecordDetailVo;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.second.service.SecondGoodsService;
+
+/**
+ * 二手商品管理后台控制器
+ */
+@Slf4j
+@Api(tags = {"二手平台-商品管理(管理后台)"})
+@ApiSort(2)
+@CrossOrigin
+@RestController
+@RequestMapping("/admin/secondGoods")
+@RequiredArgsConstructor
+public class AdminSecondGoodsController {
+
+    /**
+     * 二手商品服务
+     */
+    private final SecondGoodsService secondGoodsService;
+
+    /**
+     * 管理后台商品列表接口
+     */
+    @PostMapping("/list")
+    @ApiOperation("管理后台商品列表")
+    public R<IPage<SecondGoodsVo>> getAdminGoodsList(@ApiParam("查询参数") @RequestBody SecondGoodsAdminQueryDTO queryDTO) {
+        log.info("AdminSecondGoodsController.getAdminGoodsList?queryDTO={}", queryDTO);
+
+        // 构建分页对象
+        IPage<SecondGoodsVo> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
+
+        // 执行分页查询
+        IPage<SecondGoodsVo> result = secondGoodsService.getAdminGoodsList(page, queryDTO);
+        
+        return R.data(result, "查询成功");
+    }
+    
+    @GetMapping("/detail")
+    @ApiOperation("管理后台商品详情")
+    public R<SecondGoodsDetailVo> getAdminGoodsDetail(@ApiParam("商品ID") @RequestParam Integer goodsId) throws Exception {
+        log.info("AdminSecondGoodsController.getAdminGoodsDetail?goodsId={}", goodsId);
+        
+        SecondGoodsDetailVo result = secondGoodsService.getAdminGoodsDetail(goodsId);
+        
+        return R.data(result, "查询成功");
+    }
+
+    @GetMapping("/record/detail")
+    @ApiOperation("管理后台商品操作记录详情")
+    public R<SecondGoodsRecordDetailVo> getAdminGoodsRecordDetail(@ApiParam("操作记录ID") @RequestParam Integer recordId) {
+        log.info("AdminSecondGoodsController.getAdminGoodsRecordDetail?recordId={}", recordId);
+
+        SecondGoodsRecordDetailVo result = secondGoodsService.getAdminGoodsRecordDetail(recordId);
+
+        return R.data(result, "查询成功");
+    }
+}

+ 40 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/RiskControlController.java

@@ -0,0 +1,40 @@
+package shop.alien.second.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.second.service.RiskControlService;
+
+@RestController
+@RequestMapping("/riskControl")
+@RequiredArgsConstructor
+public class RiskControlController {
+    
+    private final RiskControlService riskControlService;
+    
+    /**
+     * 记录风控数据
+     *
+     * @param userId     用户ID
+     * @param ruleType   规则类型 1:洗钱嫌疑 2:账号异常 3:交易欺诈 4:异常发布
+     * @param ruleName   规则名称
+     * @param businessId 业务ID
+     * @param detailInfo 详细信息(JSON格式)
+     */
+    @PostMapping("/record")
+    public void recordRiskControlData(@RequestParam("userId") Integer userId,
+                                      @RequestParam("ruleType") Integer ruleType,
+                                      @RequestParam("ruleName") String ruleName,
+                                      @RequestParam("businessId") String businessId,
+                                      @RequestParam("detailInfo") String detailInfo) {
+        riskControlService.recordRiskControlData(userId, ruleType, ruleName, businessId, detailInfo);
+    }
+
+    /**
+     * 发送封禁通知
+     */
+    @GetMapping("/sendNotice")
+    public void sendNotice(Integer userId) {
+        riskControlService.sendNotice(userId);
+    }
+
+}

+ 69 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/RiskControlGoodsController.java

@@ -0,0 +1,69 @@
+package shop.alien.second.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.vo.*;
+import shop.alien.second.service.RiskControlGoodsService;
+import shop.alien.second.service.SecondGoodsService;
+
+import java.util.List;
+
+/**
+ * 风控商品信息控制器
+ */
+@Slf4j
+@Api(tags = {"二手平台-风控商品信息管理(管理后台)"})
+@ApiSort(2)
+@CrossOrigin
+@RestController
+@RequestMapping("/admin/riskControl")
+@RequiredArgsConstructor
+public class RiskControlGoodsController {
+
+    private final RiskControlGoodsService riskControlGoodsService;
+
+    /**
+     * 二手商品服务
+     */
+    private final SecondGoodsService secondGoodsService;
+    /**
+     * 通过商品记录表ID批量查询商品信息(包含图片)
+     *
+     * @param dto 商品记录表ID列表DTO
+     * @return 商品信息列表
+     */
+    @PostMapping("/goods/recordIds")
+    @ApiOperation("通过商品记录表ID批量查询商品信息")
+    public List<SecondGoodsRecordDetailVo> getGoodsInfoByRecordIds(@RequestBody RiskControlGoodsIdsDTO dto) {
+        return riskControlGoodsService.getGoodsInfoByRecordIds(dto.getRecordIds());
+    }
+
+    /**
+     * 通过商品表ID批量查询商品信息(包含图片)
+     *
+     * @param dto 商品表ID列表DTO
+     * @return 商品信息列表
+     */
+    @PostMapping("/goods/goodsIds")
+    @ApiOperation("通过商品表ID批量查询商品信息")
+    public List<SecondGoodsVo> getGoodsInfoByGoodsIds(@RequestBody RiskControlGoodsQueryDTO dto) {
+        return riskControlGoodsService.getGoodsInfoByGoodsIds(dto.getGoodsIds());
+    }
+
+
+    @PostMapping("/batch/unshelve")
+    @ApiOperation("根据风控记录批量下架商品")
+    public R<Boolean> batchUnshelveGoodsByRiskControl(@ApiParam("批量下架参数") @RequestBody BatchUnshelveGoodsDTO dto) {
+        log.info("AdminSecondGoodsController.batchUnshelveGoodsByRiskControl?dto={}", dto);
+
+        boolean result = secondGoodsService.batchShelveGoodsByRiskControlRecord(dto.getRuleType(), dto.getBusinessId());
+
+        return R.data(result, result ? "批量下架成功" : "批量下架失败");
+    }
+}

+ 84 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SearchGoodsController.java

@@ -0,0 +1,84 @@
+package shop.alien.second.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.second.service.SecondGoodsService;
+import shop.alien.util.common.JwtUtil;
+
+import java.util.List;
+
+/**
+ * 二手商品控制器
+ */
+@Slf4j
+@Api(tags = {"二手平台-商品管理-搜索相关"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/secondGoods")
+@RequiredArgsConstructor
+public class SearchGoodsController {
+
+    private final SecondGoodsService secondGoodsService;
+
+
+    /**
+     * 搜索商品列表(包含商品信息、图片、用户信息),按距离和创建时间倒序
+     */
+    @PostMapping("/search")
+    @ApiOperation("搜索结果-商品列表")
+    public R<IPage<SecondGoodsVo>> searchGoodsList(
+            @ApiParam("二手商品搜索条件") @RequestBody SecondGoodsVo secondGoodsVo) {
+        log.info("SecondGoodsController.searchGoodsList?secondGoodsVo={}", secondGoodsVo.toString());
+        R<IPage<SecondGoodsVo>> result = new R<>();
+        IPage<SecondGoodsVo> page = new Page<>(secondGoodsVo.getPageNum(), secondGoodsVo.getPageSize());
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+            String phone = data.getString("phone");
+            secondGoodsVo.setUserPhone(phone);
+            result = R.data(secondGoodsService.searchGoodsList(page, userId,secondGoodsVo), "查询成功");
+        }
+        return result;
+    }
+
+    /**
+     * 获取商品热卖排行榜(前10名)
+     */
+    @GetMapping("/getHotSellingRanking")
+    @ApiOperation("推荐 - 获取商品点赞热卖排行榜(前10名)")
+    public R<List<SecondGoods>> getHotSellingRanking() {
+        return R.data(secondGoodsService.getHotSellingRankingTop10(), "获取成功");
+    }
+
+    /**
+     * 大家都在看(前10名)
+     */
+    @GetMapping("/getCollectTop10")
+    @ApiOperation("大家都在看 - 获取商品收藏排行榜(前10名)")
+    public R<List<SecondGoods>> getCollectTop10() {
+        return R.data(secondGoodsService.getCollectTop10(), "获取成功");
+    }
+
+    /**
+     * 查询商品列表
+     */
+    @GetMapping("/getGoodsListByUserId")
+    @ApiOperation("查询商品列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "goodsStatus", value = "商品状态 0:草稿 1:审核中 2:审核失败 3:已上架 4:已下架 5:已售出", dataType = "Integer", paramType = "query")})
+    public R<List<SecondGoods>> getGoodsListByUserId(Integer userId, Integer goodsStatus) {
+        return R.data(secondGoodsService.getGoodsListByUserId(userId, goodsStatus), "获取成功");
+    }
+
+}

+ 206 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SecondEntrustUserController.java

@@ -0,0 +1,206 @@
+package shop.alien.second.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondEntrustUser;
+import shop.alien.entity.second.vo.SecondEntrustUserDTO;
+import shop.alien.entity.second.vo.SecondEntrustUserDetailVo;
+import shop.alien.entity.second.vo.SecondEntrustUserQueryVo;
+import shop.alien.entity.second.vo.SecondEntrustUserResultVo;
+import shop.alien.second.service.SecondEntrustUserService;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 二手委托人信息表 前端控制器
+ * </p>
+ *
+ * @author ssk
+ * @since 2025-11-21
+ */
+@Slf4j
+@Api(tags = {"二手平台-委托人信息管理"})
+@ApiSort(10)
+@CrossOrigin
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/secondEntrustUser")
+public class SecondEntrustUserController {
+
+    private final SecondEntrustUserService secondEntrustUserService;
+
+    @ApiOperation("创建委托人信息")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/create")
+    public R<Boolean> createEntrustUser(@Validated @RequestBody SecondEntrustUserDTO dto) {
+        log.info("SecondEntrustUserController.createEntrustUser dto={}", dto);
+        try {
+            boolean result = secondEntrustUserService.createEntrustUser(dto);
+            if (result) {
+                return R.success("创建成功");
+            }
+            return R.fail("创建失败");
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.createEntrustUser error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("根据交易ID获取委托人信息")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "entrustTradeId", value = "交易ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getByTradeId")
+    public R<SecondEntrustUser> getByTradeId(@RequestParam Integer entrustTradeId) {
+        log.info("SecondEntrustUserController.getByTradeId entrustTradeId={}", entrustTradeId);
+        try {
+            SecondEntrustUser entrustUser = secondEntrustUserService.getByTradeId(entrustTradeId);
+            return R.data(entrustUser);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.getByTradeId error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("根据交易编号获取委托人信息")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "entrustTradeNo", value = "交易编号", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/getByTradeNo")
+    public R<SecondEntrustUser> getByTradeNo(@RequestParam String entrustTradeNo) {
+        log.info("SecondEntrustUserController.getByTradeNo entrustTradeNo={}", entrustTradeNo);
+        try {
+            SecondEntrustUser entrustUser = secondEntrustUserService.getByTradeNo(entrustTradeNo);
+            return R.data(entrustUser);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.getByTradeNo error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("根据ID获取委托人信息")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "委托人ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getById")
+    public R<SecondEntrustUser> getById(@RequestParam Integer id) {
+        log.info("SecondEntrustUserController.getById id={}", id);
+        try {
+            SecondEntrustUser entrustUser = secondEntrustUserService.getById(id);
+            if (entrustUser == null) {
+                return R.fail("委托人信息不存在");
+            }
+            return R.data(entrustUser);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.getById error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("更新委托人信息")
+    @ApiOperationSupport(order = 5)
+    @PutMapping("/update/{id}")
+    public R<Boolean> updateEntrustUser(
+            @ApiParam(value = "委托人ID", required = true) @PathVariable Integer id,
+            @Validated @RequestBody SecondEntrustUserDTO dto) {
+        log.info("SecondEntrustUserController.updateEntrustUser id={}, dto={}", id, dto);
+        try {
+            boolean result = secondEntrustUserService.updateEntrustUser(id, dto);
+            if (result) {
+                return R.success("更新成功");
+            }
+            return R.fail("更新失败");
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.updateEntrustUser error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("删除委托人信息")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "委托人ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @DeleteMapping("/delete")
+    public R<Boolean> deleteEntrustUser(@RequestParam Integer id) {
+        log.info("SecondEntrustUserController.deleteEntrustUser id={}", id);
+        try {
+            boolean result = secondEntrustUserService.deleteEntrustUser(id);
+            if (result) {
+                return R.success("删除成功");
+            }
+            return R.fail("删除失败");
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.deleteEntrustUser error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("根据用户电话查询委托人信息列表")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "entrustUserPhone", value = "用户电话", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/getByUserPhone")
+    public R<List<SecondEntrustUser>> getByUserPhone(@RequestParam String entrustUserPhone) {
+        log.info("SecondEntrustUserController.getByUserPhone entrustUserPhone={}", entrustUserPhone);
+        try {
+            List<SecondEntrustUser> list = secondEntrustUserService.getByUserPhone(entrustUserPhone);
+            return R.data(list);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.getByUserPhone error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("分页查询委托人信息列表")
+    @ApiOperationSupport(order = 7)
+    @PostMapping("/page")
+    public R<IPage<SecondEntrustUserResultVo>> getEntrustUserPage(@RequestBody SecondEntrustUserQueryVo queryVo) {
+        log.info("SecondEntrustUserController.getEntrustUserPage queryVo={}", queryVo);
+        try {
+            // 设置默认分页参数
+            if (queryVo.getPageNum() == null || queryVo.getPageNum() < 1) {
+                queryVo.setPageNum(1);
+            }
+            if (queryVo.getPageSize() == null || queryVo.getPageSize() < 1) {
+                queryVo.setPageSize(10);
+            }
+            
+            IPage<SecondEntrustUserResultVo> page = secondEntrustUserService.getEntrustUserPage(queryVo);
+            return R.data(page);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.getEntrustUserPage error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("根据姓名和身份证号获取委托人详情")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "entrustUserName", value = "委托人姓名", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "entrustIdCard", value = "委托人身份证号", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/detail")
+    public R<SecondEntrustUserDetailVo> getEntrustUserDetail(@RequestParam String entrustUserName, @RequestParam String entrustIdCard) {
+        log.info("SecondEntrustUserController.getEntrustUserDetail entrustUserName={}, entrustIdCard={}", entrustUserName, entrustIdCard);
+        try {
+            SecondEntrustUserDetailVo detailVo = secondEntrustUserService.getEntrustUserDetail(entrustUserName, entrustIdCard);
+            return R.data(detailVo);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.getEntrustUserDetail error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+}
+

+ 33 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SecondGaoDeController.java

@@ -0,0 +1,33 @@
+package shop.alien.second.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.second.config.GaoDeMapUtil;
+
+import java.util.Map;
+
+@Slf4j
+@Api(tags = {"二手平台-高德"})
+@ApiSort(9)
+@CrossOrigin
+@RestController
+@RequestMapping("/second/gaode")
+@RequiredArgsConstructor
+public class SecondGaoDeController {
+
+    private final GaoDeMapUtil gaodeMapUtil;
+
+    @ApiOperation("获取城市与城市半径")
+    @GetMapping("/queryCity")
+    public R<Map<String, Object>> queryCity(
+            @RequestParam(value = "longitude", required = false) String longitude,
+            @RequestParam(value = "latitude", required = false) String latitude) {
+        return R.data(gaodeMapUtil.getCityAndRadiusByLonAndLat(longitude, latitude), "查询成功");
+    }
+
+}

+ 33 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SecondGoodsCategoryController.java

@@ -0,0 +1,33 @@
+package shop.alien.second.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondGoodsCategory;
+import shop.alien.second.service.SecondGoodsCategoryService;
+
+import java.util.List;
+
+@Slf4j
+@Api(tags = {"二手平台-商品类型"})
+@ApiSort(9)
+@CrossOrigin
+@RestController
+@RequestMapping("/second/goodsCategory")
+@RequiredArgsConstructor
+public class SecondGoodsCategoryController {
+
+    private final SecondGoodsCategoryService service;
+
+    @ApiOperation("搜索商品类型")
+    @PostMapping("/querySecondGoodsByParentId")
+    public R<List<SecondGoodsCategory>> querySecondGoodsByParentId(
+            @RequestParam(value = "parentId", required = false) Integer parentId,
+            @RequestParam(value = "categoryState", required = false) Integer categoryState) throws Exception {
+        return R.data(service.querySecondGoodsByParentId(parentId, categoryState), "查询成功");
+    }
+}

+ 361 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SecondGoodsController.java

@@ -0,0 +1,361 @@
+package shop.alien.second.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.google.common.collect.Lists;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.SecondVideoTask;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.mapper.second.SecondGoodsAuditMapper;
+import shop.alien.second.service.SecondGoodsAuditService;
+import shop.alien.second.service.SecondGoodsService;
+import shop.alien.second.service.VideoModerationService;
+import shop.alien.util.common.JwtUtil;
+import shop.alien.config.filter.NoRepeatSubmit;
+import shop.alien.util.common.safe.ImageModerationUtil;
+import shop.alien.util.common.safe.TextModerationResultVO;
+import shop.alien.util.common.safe.TextModerationUtil;
+import shop.alien.util.common.safe.TextReviewServiceEnum;
+import shop.alien.util.common.safe.*;
+
+import java.util.List;
+
+/**
+ * 二手商品控制器
+ */
+@Slf4j
+@Api(tags = {"二手平台-商品管理"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/secondGoods")
+@RequiredArgsConstructor
+public class SecondGoodsController {
+
+    /**
+     * 视频审核服务
+     */
+    private final VideoModerationService videoModerationService;
+    /**
+     * 二手商品服务
+     */
+    private final SecondGoodsService secondGoodsService;
+
+    /**
+     * 二手商品审核表
+     */
+    private final SecondGoodsAuditMapper secondGoodsAuditMapper;
+
+    // 注入文本审核工具
+    private final TextModerationUtil textModerationUtil;
+
+    // 注入图片审核工具
+    private final ImageModerationUtil imageModerationUtil;
+
+    // 注入二手商品审核服务
+    private final SecondGoodsAuditService secondGoodsAuditService;
+
+    /**
+     * 根据ID获取二手商品
+     */
+    @GetMapping("/getSecondGoodsById")
+    @ApiOperation("根据ID获取二手商品 - 商品编辑回显")
+    public R<SecondGoodsVo> getSecondGoodsById(@ApiParam("根据ID获取二手商品 - 商品编辑回显") @RequestParam Integer id) {
+        log.info("SecondGoodsController.getSecondGoodsById?id={}", id);
+        return R.data(secondGoodsService.getSecondGoodsById(id), "获取成功");
+    }
+
+    /**
+     * 添加二手商品
+     */
+    @PostMapping("/save")
+    @ApiOperation("发布二手商品")
+    @NoRepeatSubmit(expireTime = 5, message = "请勿重复提交发布商品请求")
+    public R<Void> addSecondGoods(@ApiParam("二手商品信息") @RequestBody SecondGoodsVo secondGoods) throws Exception {
+        log.info("SecondGoodsController.addSecondGoods?secondGoods={}", secondGoods.toString());
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+            secondGoods.setUserId(userId);// 设置用户ID (发布者)
+        }
+        if(secondGoods.getSaveType().equals(0)){
+            if (!secondGoodsService.saveAsDraft(secondGoods)) {
+                return R.fail("保存商品为草稿失败");
+            }
+            return R.success("保存商品为草稿成功");
+        } else {
+            // 添加商品 0 创建
+            if (!secondGoodsService.createBasicInfo(secondGoods,0)) {
+                return R.fail("添加二手商品失败");
+            }
+            return R.success("添加二手商品成功");
+        }
+    }
+
+    /**
+     * 更新二手商品
+     */
+    @PostMapping("/edit")
+    @ApiOperation("更新二手商品")
+    @NoRepeatSubmit(expireTime = 5, message = "请勿重复提交更新商品请求")
+    public R<Void> updateSecondGoods(@ApiParam("二手商品信息") @RequestBody SecondGoodsVo secondGoods) throws Exception {
+        log.info("SecondGoodsController.updateSecondGoods?secondGoods={}", secondGoods.toString());
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+            secondGoods.setUserId(userId);// 设置用户ID (发布者)
+        }
+        // 添加商品 0 创建 1 更新
+        if (!secondGoodsService.createBasicInfo(secondGoods,1)) {
+            return R.fail("修改二手商品失败");
+        }
+        return R.success("修改二手商品成功");
+    }
+
+
+    /**
+     * 下架二手商品
+     */
+    @PostMapping("/shelve")
+    @ApiOperation("下架二手商品")
+    @NoRepeatSubmit(expireTime = 3, message = "请勿重复提交下架商品请求")
+    public R<Void> shelveSecondGoods(@ApiParam("下架二手商品") @RequestBody SecondGoodsVo secondGoods) {
+        log.info("SecondGoodsController.shelveSecondGoods?secondGoods={}", secondGoods.toString());
+        boolean shelveResult = secondGoodsService.shelveSecondGoods(secondGoods);
+        if (shelveResult) {
+            return R.success("下架成功");
+        } else {
+            return R.fail("下架失败");
+        }
+    }
+
+    /**
+     * 删除二手商品
+     */
+    @PostMapping("/delete")
+    @ApiOperation("删除二手商品")
+    @NoRepeatSubmit(expireTime = 3, message = "请勿重复提交删除商品请求")
+    public R<Void> deleteSecondGoods(@ApiParam("删除二手商品") @RequestBody SecondGoodsVo secondGoods) {
+        log.info("SecondGoodsController.deleteSecondGoods?secondGoods={}", secondGoods.toString());
+        SecondGoods goods = secondGoodsService.getById(secondGoods.getId());
+        if (goods != null) {
+            boolean removeResult = secondGoodsService.removeById(secondGoods.getId());
+            if (removeResult) {
+                // 设置删除标记并记录操作历史
+                goods.setDeleteFlag(1);
+                secondGoodsService.recordGoodsOperation(goods,"删除商品");
+                return R.success("删除成功");
+            } else {
+                return R.fail("删除失败");
+            }
+        } else {
+            return R.fail("商品不存在");
+        }
+    }
+
+    /**
+     * 商品审核状态查询-消息跳转用
+     */
+    @GetMapping("/getGoodsAuditStatus")
+    @ApiOperation("商品审核状态查询-消息跳转用(商品状态 0:草稿 1:审核中 2:审核失败 3:已上架 4:已下架 5:已售出)")
+    public R<SecondGoods> getGoodsAuditStatus(@ApiParam("商品审核id") @RequestParam Integer goodsId) {
+        log.info("SecondGoodsController.getGoodsAuditStatus?goodsId={}", goodsId);
+//        Integer goodsId = secondGoodsAuditMapper.selectById(auditId).getGoodsId();
+        if (goodsId != null){
+            // 获取商品审核状态
+            QueryWrapper<SecondGoods> queryWrapper = new QueryWrapper<>();
+            queryWrapper.lambda()
+                    .eq(SecondGoods::getId, goodsId);
+            return R.data(secondGoodsService.getOne(queryWrapper));
+        }else {
+            return R.fail("商品不存在");
+        }
+    }
+
+    /**
+     * 商品热度添加接口
+     */
+    @PostMapping("/addGoodsHot")
+    @ApiOperation("商品热度添加,热度数量加1")
+    public R<Void> addGoodsHot(@ApiParam("商品ID") @RequestParam Integer goodsId) {
+        log.info("SecondGoodsController.addGoodsHot?goodsId={}", goodsId);
+        if (goodsId == null) {
+            return R.fail("商品ID不能为空");
+        }
+        
+        SecondGoods goods = secondGoodsService.getById(goodsId);
+        if (goods == null) {
+            return R.fail("商品不存在");
+        }
+        
+        // 热度数量加1(使用likeCount字段表示热度)
+        SecondGoods updateGoods = new SecondGoods();
+        updateGoods.setId(goodsId);
+        updateGoods.setLikeCount(goods.getLikeCount() == null ? 1 : goods.getLikeCount() + 1);
+        boolean result = secondGoodsService.updateById(updateGoods);
+        
+        if (result) {
+            return R.success("热度添加成功");
+        } else {
+            return R.fail("热度添加失败");
+        }
+    }
+
+    /**
+     * 文本审核接口 - 商品发布场景
+     */
+    @GetMapping("/textModeration/productPublish")
+    @ApiOperation("文本审核 - 商品发布场景(大语言模型输入文字检测、广告法合规检测_专业版)")
+    public R<TextModerationResultVO> productPublishTextModeration(@ApiParam("待审核文本") @RequestParam String text) {
+        log.info("SecondGoodsController.productPublishTextModeration?text={}", text);
+        try {
+            List<String> servicesList = Lists.newArrayList();
+            servicesList.add(TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService());
+            servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
+            TextModerationResultVO textModerationResultVO = textModerationUtil.invokeFunction(text,servicesList);
+            return R.data(textModerationResultVO);
+        } catch (Exception e) {
+            log.error("文本审核异常", e);
+            return R.fail("文本审核异常: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 文本审核接口 - 聊一聊场景
+     */
+    @GetMapping("/textModeration/chat")
+    @ApiOperation("文本审核 - 聊一聊场景(私聊互动内容检测_专业版、广告法合规检测_专业版)")
+    public R<TextModerationResultVO> chatTextModeration(@ApiParam("待审核文本") @RequestParam String text) {
+        log.info("SecondGoodsController.chatTextModeration?text={}", text);
+        try {
+            List<String> servicesList = Lists.newArrayList();
+            servicesList.add(TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService());
+            servicesList.add(TextReviewServiceEnum.CHAT_DETECTION_PRO.getService());
+            TextModerationResultVO textModerationResultVO = textModerationUtil.invokeFunction(text,servicesList);
+            return R.data(textModerationResultVO);
+        } catch (Exception e) {
+            log.error("文本审核异常", e);
+            return R.fail("文本审核异常: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 文本审核接口 - 评论场景
+     */
+    @GetMapping("/textModeration/comment")
+    @ApiOperation("文本审核 - 评论场景(公聊评论内容检测_专业版、广告法合规检测_专业版)")
+    public R<TextModerationResultVO> commentTextModeration(@ApiParam("待审核文本") @RequestParam String text) {
+        log.info("SecondGoodsController.commentTextModeration?text={}", text);
+        try {
+            List<String> servicesList = Lists.newArrayList();
+            servicesList.add(TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService());
+            servicesList.add(TextReviewServiceEnum.CHAT_DETECTION_PRO.getService());
+            TextModerationResultVO textModerationResultVO = textModerationUtil.invokeFunction(text,servicesList);
+            return R.data(textModerationResultVO);
+        } catch (Exception e) {
+            log.error("文本审核异常", e);
+            return R.fail("文本审核异常: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 图片审核接口 - 发布商品场景
+     */
+    @GetMapping("/imageModeration/productPublish")
+    @ApiOperation("图片审核 - 发布商品场景(内容治理检测、AIGC图片风险检测)")
+    public R<ImageModerationResultVO> productPublishImageModeration(@ApiParam("图片地址") @RequestParam String imageUrl) {
+        log.info("SecondGoodsController.productPublishImageModeration?imageUrl={}", imageUrl);
+        try {
+            List<String> servicesList = Lists.newArrayList();
+            // TODO 后续配置到数据库 中
+            servicesList.add(ImageReviewServiceEnum.TONALITY_IMPROVE.getService());
+            servicesList.add(ImageReviewServiceEnum.AIGC_CHECK.getService());
+            ImageModerationResultVO response = imageModerationUtil.productPublishCheck(imageUrl,servicesList);
+            return R.data(response);
+        } catch (Exception e) {
+            log.error("图片审核异常", e);
+            return R.fail("图片审核异常: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 图片审核接口 - 聊一聊场景
+     */
+    @GetMapping("/imageModeration/chat")
+    @ApiOperation("图片审核 - 聊一聊场景(内容治理检测)")
+    public R<ImageModerationResultVO> chatImageModeration(@ApiParam("图片地址") @RequestParam String imageUrl) {
+        log.info("SecondGoodsController.chatImageModeration?imageUrl={}", imageUrl);
+        try {
+            List<String> servicesList = Lists.newArrayList();
+            // TODO 后续配置到数据库 中
+            servicesList.add(ImageReviewServiceEnum.TONALITY_IMPROVE.getService());
+            ImageModerationResultVO response = imageModerationUtil.productPublishCheck(imageUrl,servicesList);
+            return R.data(response, "审核成功");
+        } catch (Exception e) {
+            log.error("图片审核异常", e);
+            return R.fail("图片审核异常: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 处理视频审核结果
+     * @param taskId 视频审核任务ID
+     * @return 处理结果
+     */
+    @ApiOperation("处理视频审核结果")
+    @GetMapping("/processVideoResult")
+    public boolean processVideoResult(String taskId) {
+        try {
+            SecondVideoTask task = videoModerationService.getTaskByTaskId(taskId);
+            if (task == null) {
+                return false;
+            }
+            secondGoodsService.processVideoModerationResult(task);
+            return true;
+        } catch (Exception e) {
+            log.error("处理视频审核结果时发生异常,任务ID: {}", taskId, e);
+            return false;
+        }
+    }
+
+    /**
+     * 获取AI商品审核结果
+     * 查询所有状态为处理中的AI审核任务,并更新审核结果
+     * @return 处理结果
+     */
+    @ApiOperation("获取AI商品审核结果")
+    @GetMapping("/getAiGoodsCheckResult")
+    public R<String> getAiGoodsCheckResult() {
+        log.info("SecondGoodsController.getAiGoodsCheckResult");
+        try {
+            String result = secondGoodsAuditService.getAiGoodsCheckResult();
+            return R.data(result, "获取AI审核结果完成");
+        } catch (Exception e) {
+            log.error("获取AI商品审核结果异常", e);
+            return R.fail("获取AI商品审核结果异常: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 处理视频审核结果
+     * 查询所有待处理的视频审核任务,拉取审核结果并更新商品状态
+     * @return 处理结果
+     */
+    @ApiOperation("处理所有待处理的视频审核任务")
+    @GetMapping("/processVideoModerationResult")
+    public R<String> processVideoModerationResult() {
+        log.info("SecondGoodsController.processVideoModerationResult");
+        try {
+            String result = videoModerationService.processAllPendingVideoTasks();
+            return R.data(result, "视频审核结果处理完成");
+        } catch (Exception e) {
+            log.error("处理视频审核结果异常", e);
+            return R.fail("处理视频审核结果异常: " + e.getMessage());
+        }
+    }
+}

+ 43 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SecondGoodsReportingController.java

@@ -0,0 +1,43 @@
+package shop.alien.second.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.vo.SecondReportingVo;
+import shop.alien.entity.second.vo.SecondUserViolationVo;
+import shop.alien.second.service.SecondGoodsReportingService;
+
+@Slf4j
+@Api(tags = {"二手平台-举报详情"})
+@ApiSort(9)
+@CrossOrigin
+@RestController
+@RequestMapping("/second/reporting")
+@RequiredArgsConstructor
+public class SecondGoodsReportingController {
+
+    private final SecondGoodsReportingService service;
+
+    @ApiOperation("举报详情")
+    @PostMapping("/queryReportingDetail")
+    public R<SecondReportingVo> queryReportingDetail(@RequestParam(value = "id", required = false) Integer id) {
+        log.info("SecondGoodsReportingController.reportingDetail?id={}", id);
+        return R.data(service.queryReportingDetail(id), "查询成功");
+    }
+
+    @ApiOperation("举报")
+    @PostMapping("/addReport")
+    public R<String> reporting(@RequestBody SecondUserViolationVo lifeuserViolation) throws Exception {
+        log.info("SecondGoodsReportingController.reporting?lifeuserViolation={}", lifeuserViolation);
+        int num = service.reporting(lifeuserViolation);
+        if (0 == num) {
+            return R.fail("举报失败");
+        }
+        return R.data("举报成功");
+    }
+
+}

+ 78 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SecondRecommendController.java

@@ -0,0 +1,78 @@
+package shop.alien.second.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.vo.SecondGoodsRecommendVo;
+import shop.alien.second.service.SecondRecommendService;
+
+@Slf4j
+@Api(tags = {"二手平台-商品列表"})
+@ApiSort(9)
+@CrossOrigin
+@RestController
+@RequestMapping("/recommend")
+@RequiredArgsConstructor
+public class SecondRecommendController {
+
+    private final SecondRecommendService service;
+
+    @ApiOperation("搜索推荐商品列表")
+    @PostMapping("/queryRecommendList")
+    public R<IPage<SecondGoodsRecommendVo>> queryRecommendList(
+            @RequestParam(value = "pageNum", required = false) Integer pageNum,
+            @RequestParam(value = "pageSize", required = false) Integer pageSize,
+            @RequestParam(value = "typeId", required = false) Integer typeId,
+            @RequestParam(value = "longitude", required = false) String longitude,
+            @RequestParam(value = "latitude", required = false) String latitude,
+            @RequestParam(value = "radiusKm", required = false) String radiusKm) throws Exception {
+        log.info("LifeCollectController.cancelCollect?pageNum={},pageSize={},longitude={},latitude={},typeId={}", pageNum, pageSize, longitude, latitude, typeId);
+        IPage<SecondGoodsRecommendVo> page = new Page<>(pageNum, pageSize);
+        IPage<SecondGoodsRecommendVo> result = service.getSecondRecommendByPage(page, longitude, latitude, typeId, radiusKm);
+        return R.data(result, "查询成功");
+    }
+
+    @ApiOperation("搜索关注商品列表")
+    @PostMapping("/querySecondConcernByPage")
+    public R<IPage<SecondGoodsRecommendVo>> querySecondConcernByPage(
+            @RequestParam(value = "pageNum", required = false) Integer pageNum,
+            @RequestParam(value = "pageSize", required = false) Integer pageSize,
+            @RequestParam(value = "longitude", required = false) String longitude,
+            @RequestParam(value = "latitude", required = false) String latitude) throws Exception {
+        log.info("LifeCollectController.cancelCollect?pageNum={},pageSize={},longitude={},latitude={}", pageNum, pageSize, longitude, latitude);
+        IPage<SecondGoodsRecommendVo> page = new Page<>(pageNum, pageSize);
+        IPage<SecondGoodsRecommendVo> result = service.querySecondConcernByPage(page, longitude + "," + latitude);
+        return R.data(result, "查询成功");
+    }
+
+    @ApiOperation("搜索新品商品列表")
+    @PostMapping("/querySecondNewGoodsByPage")
+    public R<IPage<SecondGoodsRecommendVo>> querySecondNewGoodsByPage(
+            @RequestParam(value = "pageNum", required = false) Integer pageNum,
+            @RequestParam(value = "pageSize", required = false) Integer pageSize,
+            @RequestParam(value = "longitude", required = false) String longitude,
+            @RequestParam(value = "latitude", required = false) String latitude,
+            @RequestParam(value = "radiusKm", required = false) String radiusKm) throws Exception {
+        log.info("LifeCollectController.cancelCollect?pageNum={},pageSize={},longitude={},latitude={}", pageNum, pageSize, longitude, latitude);
+        IPage<SecondGoodsRecommendVo> page = new Page<>(pageNum, pageSize);
+        IPage<SecondGoodsRecommendVo> result = service.querySecondNewGoodsByPage(page, longitude + "," + latitude, radiusKm);
+        return R.data(result, "查询成功");
+    }
+
+    @ApiOperation("搜索商品详情")
+    @PostMapping("/querySecondGoodsDetail")
+    public R<SecondGoodsRecommendVo> querySecondGoodsDetail(
+            @RequestParam(value = "goodsId", required = false) Integer goodsId,
+            @RequestParam(value = "longitude", required = false) String longitude,
+            @RequestParam(value = "latitude", required = false) String latitude) throws Exception {
+        log.info("LifeCollectController.cancelCollect?goodsId={},longitude={},latitude={}", goodsId, longitude, latitude);
+        return R.data(service.querySecondGoodsDetail(goodsId, longitude + "," + latitude), "查询成功");
+    }
+
+}

+ 72 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SecondShieldController.java

@@ -0,0 +1,72 @@
+package shop.alien.second.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.formula.functions.T;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondShield;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.mapper.second.SecondShieldMapper;
+import shop.alien.util.common.JwtUtil;
+
+import java.util.Date;
+
+@Slf4j
+@Api(tags = {"二手平台-拉黑"})
+@ApiSort(9)
+@CrossOrigin
+@RestController
+@RequestMapping("/second/shield")
+@RequiredArgsConstructor
+public class SecondShieldController {
+
+    private final SecondShieldMapper mapper;
+
+    @ApiOperation("商品拉黑")
+    @PostMapping("/saveGoodsShield")
+    public R<T> saveGoodsShield(
+            @RequestParam(value = "shieldId", required = false) Integer shieldId) {
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        Integer userId = null;
+        if (data != null) {
+            userId = data.getInteger("userId");
+        }
+        if (userId == null) {
+            return null;
+        }
+        SecondShield shield = new SecondShield();
+        shield.setUserId(userId);
+        shield.setShieldId(shieldId);
+        shield.setShieldType(1);
+        shield.setCreatedUserId(userId);
+        shield.setCreatedTime(new Date());
+        shield.setUpdatedUserId(userId);
+        shield.setUpdatedTime(new Date());
+        Integer num = mapper.insert(shield);
+
+        if(num == 1) {
+            return R.success("拉黑成功");
+        }
+        return R.fail("拉黑失败");
+
+    }
+
+
+    /**
+     * 取消拉黑
+     */
+    @PostMapping("/deleteGoodsShield")
+    @ApiOperation("取消拉黑")
+    public R<Void> deleteGoodsShield(@ApiParam("取消拉黑") @RequestBody SecondGoodsVo secondGoods) {
+        log.info("SecondShieldController.GoodsShield?secondGoods={}", secondGoods.toString());
+        mapper.deleteById(secondGoods.getShieldId());
+        return R.success("取消拉黑成功");
+    }
+
+}

+ 221 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SecondTradeRecordController.java

@@ -0,0 +1,221 @@
+package shop.alien.second.controller;
+
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.BusinessException;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondTradeRecord;
+import shop.alien.entity.second.vo.SecondEntrustUserDTO;
+import shop.alien.entity.second.vo.SecondTradeRecordVo;
+import shop.alien.entity.second.vo.SellerEvaluationVo;
+import shop.alien.second.service.SecondTradeRecordService;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 二手交易记录表 前端控制器
+ * </p>
+ *
+ * @author qrs
+ * @since 2025-07-07
+ */
+@Slf4j
+@Api(tags = {"二手平台-商品交易记录"})
+@ApiSort(1)
+@CrossOrigin
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/secondTradeRecord")
+public class SecondTradeRecordController {
+
+    private final SecondTradeRecordService secondTradeRecordService;
+
+    @ApiOperation("创建交易")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/createTrade")
+    public R<Boolean> createTrade(@RequestBody SecondEntrustUserDTO entity) throws Exception {
+        log.info("SecondTradeRecordController.createTrade?entity={}", entity.toString());
+        if (secondTradeRecordService.createTrade(entity)) return R.success("创建成功");
+        return R.fail("创建失败");
+    }
+
+    @ApiOperation("确认商品是否可以交易")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({@ApiImplicitParam(name = "goodsId", value = "商品id", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/goodsTradeConfirm")
+    public R<Boolean> goodsTradeConfirm(@RequestParam int goodsId) throws Exception {
+        log.info("SecondTradeRecordController.goodsTradeConfirm?goodsId={}", goodsId);
+        return R.data(secondTradeRecordService.goodsTradeConfirm(goodsId));
+    }
+
+    @ApiOperation("确认/拒绝交易")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "tradeId", value = "交易id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "messageId", value = "消息id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "0-拒绝  1-确认", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/tradeConfirm")
+    public R<Boolean> tradeConfirm(@RequestParam int tradeId, @RequestParam int messageId, @RequestParam int type) {
+        log.info("SecondTradeRecordController.tradeConfirm?tradeId={}, messageId={}, type={}", tradeId, messageId, type);
+        try {
+            secondTradeRecordService.tradeConfirm(tradeId, messageId, type);
+            return R.success("操作成功");
+        } catch (BusinessException e) {
+            return R.fail(e.getCause().getMessage());
+        }  catch (Exception e) {
+            return R.fail("系统繁忙,请稍后再试");
+        }
+    }
+
+    @ApiOperation("取消交易")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "tradeId", value = "交易id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "cancelReason", value = "取消原因", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "cancelReasonSupplement", value = "取消原因补充", dataType = "String", paramType = "query")})
+    @GetMapping("/cancelTrade")
+    public R<Boolean> cancelTrade(@RequestParam int tradeId, @RequestParam String cancelReason, String cancelReasonSupplement) throws Exception {
+        log.info("SecondTradeRecordController.cancelTrade?tradeId={}, cancelReason={}, cancelReasonSupplement={}", tradeId, cancelReason, cancelReasonSupplement);
+        if (secondTradeRecordService.cancelTrade(tradeId, cancelReason, cancelReasonSupplement)) return R.success("取消交易成功");
+        return R.fail("取消交易失败");
+    }
+
+    @ApiOperation("交易是否可以签到")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({@ApiImplicitParam(name = "tradeId", value = "交易id", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/whetherCanSignIn")
+    public R<Boolean> whetherCanSignIn(@RequestParam int tradeId) throws Exception {
+        log.info("SecondTradeRecordController.whetherCanSignIn?tradeId={}", tradeId);
+        return R.data(secondTradeRecordService.whetherCanSignIn(tradeId));
+    }
+
+    @ApiOperation("交易签到")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "tradeId", value = "交易id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "messageId", value = "消息id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "signInLatitudeLongitude", value = "签到地点(经纬度)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "signInLatitudeLongitudeAddress", value = "签到地点(经纬度地址)", dataType = "String", paramType = "query", required = true)})
+    @GetMapping("/tradeSignIn")
+    public R<Boolean> tradeSignIn(@RequestParam int tradeId, int messageId, String signInLatitudeLongitude, String signInLatitudeLongitudeAddress) {
+        log.info("SecondTradeRecordController.tradeSignIn?tradeId={}, messageId={}", tradeId, messageId);
+        try {
+            secondTradeRecordService.tradeSignIn(tradeId, messageId, signInLatitudeLongitude, signInLatitudeLongitudeAddress);
+            return R.success("签到成功");
+        } catch (BusinessException e) {
+            return R.fail(e.getCause().getMessage());
+        }  catch (Exception e) {
+            return R.fail("系统繁忙,请稍后再试");
+        }
+    }
+
+    @ApiOperation("交易完成确认")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "tradeId", value = "交易id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "1-交易成功  2-交易失败", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "evaluate", value = "评价", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "rating", value = "评分", dataType = "Integer", paramType = "query")})
+    @GetMapping("/tradeCompleteConfirm")
+    public R<Boolean> tradeCompleteConfirm(@RequestParam int tradeId, @RequestParam int type, String evaluate, Integer rating) throws Exception {
+        log.info("SecondTradeRecordController.tradeCompleteConfirm?tradeId={}, type={}, evaluate={}, rating={}", tradeId, type, evaluate, rating);
+        return R.data(secondTradeRecordService.tradeCompleteConfirm(tradeId, type, evaluate, rating));
+    }
+
+    @ApiOperation("获取用户交易确认信息")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({@ApiImplicitParam(name = "tradeId", value = "交易id", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/getUserTradeStatus")
+    public R<SecondTradeRecordVo> getUserTradeStatus(@RequestParam int tradeId) throws Exception {
+        log.info("SecondTradeRecordController.getUserTradeStatus?tradeId={}", tradeId);
+        return R.data(secondTradeRecordService.getUserTradeStatus(tradeId));
+    }
+
+    @ApiOperation("交易窗")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({@ApiImplicitParam(name = "sideId", value = "对方的id", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/getTradeRecord")
+    public R<List<SecondTradeRecordVo>> getTradeRecord(@RequestParam int sideId) throws Exception {
+        log.info("SecondTradeRecordController.getTradeRecord?sideId={}", sideId);
+        return R.data(secondTradeRecordService.getTradeRecord(sideId));
+    }
+
+    @ApiOperation("双方是否有交易中的状态")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({@ApiImplicitParam(name = "sideId", value = "对方的id", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/hasInTradeRecord")
+    public R<SecondTradeRecord> hasInTradeRecord(@RequestParam int sideId) throws Exception {
+        log.info("SecondTradeRecordController.hasInTradeRecord?sideId={}", sideId);
+        return R.data(secondTradeRecordService.hasInTradeRecord(sideId));
+    }
+
+    @ApiOperation("修改交易信息")
+    @ApiOperationSupport(order = 11)
+    @ApiImplicitParams({@ApiImplicitParam(name = "type", value = "1-确认  2-拒绝", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/modifyTradeRecord")
+    public R<Boolean> modifyTradeRecord(int type, Integer tradeId, String transactionTime,
+                                        String transactionLatitudeLongitude,
+                                        String transactionLatitudeLongitudeAddress,
+                                        String transactionLocation,
+                                        String messageId, String entrustUserPhone, String entrustUserName, String entrustIdCard, Integer entrustId, String entrustIdCardImg) throws Exception {
+        log.info("SecondTradeRecordController.modifyTradeRecord?record={}", transactionTime);
+        return R.data(secondTradeRecordService.modifyTradeRecord(type, tradeId, transactionTime, transactionLatitudeLongitude, transactionLatitudeLongitudeAddress, transactionLocation, messageId, entrustUserPhone, entrustUserName, entrustIdCard, entrustId, entrustIdCardImg));
+    }
+
+    @ApiOperation("获取用户作为卖家的交易评价列表(分页)")
+    @ApiOperationSupport(order = 12)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "sellerId", value = "卖家id(用户主页展示用,必传)", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "Integer", paramType = "query", required = false, defaultValue = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "每页条数", dataType = "Integer", paramType = "query", required = false, defaultValue = "10")
+    })
+    @GetMapping("/getSellerEvaluationList")
+    public R<IPage<SellerEvaluationVo>> getSellerEvaluationList(
+            @RequestParam(required = true) Integer sellerId,
+            @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+            @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        log.info("SecondTradeRecordController.getSellerEvaluationList?sellerId={}, pageNum={}, pageSize={}", 
+                sellerId, pageNum, pageSize);
+        try {
+            IPage<SellerEvaluationVo> evaluationPage = secondTradeRecordService.getSellerEvaluationList(sellerId, pageNum, pageSize);
+            return R.data(evaluationPage);
+        } catch (Exception e) {
+            log.error("SecondTradeRecordController.getSellerEvaluationList?error: {}", e.getMessage(), e);
+            return R.fail("获取评价列表失败: " + e.getMessage());
+        }
+    }
+
+    @ApiOperation("加入定位")
+    @ApiOperationSupport(order = 13)
+    @ApiImplicitParams({@ApiImplicitParam(name = "otherUserId", value = "对方userId", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/locationShareAdd")
+    public R<String> locationShareAdd(Integer otherUserId) throws Exception {
+        log.info("SecondTradeRecordController.locationShareAdd?otherUserId={}", otherUserId);
+        secondTradeRecordService.locationShareAdd(otherUserId);
+        return R.data("操作成功");
+    }
+
+    @ApiOperation("退出定位")
+    @ApiOperationSupport(order = 14)
+    @ApiImplicitParams({@ApiImplicitParam(name = "otherUserId", value = "对方userId", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/locationShareDel")
+    public R<String> locationShareDel(Integer otherUserId) throws Exception {
+        log.info("SecondTradeRecordController.locationShareDel?otherUserId={}", otherUserId);
+        secondTradeRecordService.locationShareDel(otherUserId);
+        return R.success("操作成功");
+    }
+
+    @ApiOperation("是否在定位里面")
+    @ApiOperationSupport(order = 15)
+    @ApiImplicitParams({@ApiImplicitParam(name = "otherUserId", value = "对方userId", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/locationShareHas")
+    public R<Boolean> locationShareHas(Integer otherUserId) throws Exception {
+        log.info("SecondTradeRecordController.locationShareHas?otherUserId={}", otherUserId);
+        return R.data(secondTradeRecordService.locationShareHas(otherUserId));
+    }
+}

+ 97 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SecondUserCreditController.java

@@ -0,0 +1,97 @@
+package shop.alien.second.controller;
+
+import com.alibaba.fastjson2.JSONObject;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondUserCredit;
+import shop.alien.entity.second.vo.SecondUserCreditVo;
+import shop.alien.second.service.SecondUserCreditService;
+
+/**
+ * 二手平台用户积分控制器
+ */
+@Slf4j
+@Api(tags = {"二期-用户积分"})
+@CrossOrigin
+@RestController
+@RequestMapping("/userPoints")
+@RequiredArgsConstructor
+public class SecondUserCreditController {
+    
+    private final SecondUserCreditService secondUserCreditService;
+    
+    @ApiOperation("获取用户积分")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/{userId}")
+    public R<SecondUserCredit> getUserPoints(@PathVariable Integer userId) {
+        log.info("获取用户积分,userId={}", userId);
+        try {
+            SecondUserCredit userPoints = secondUserCreditService.getByUserId(userId);
+            return R.data(userPoints);
+        } catch (Exception e) {
+            log.error("获取用户积分失败,userId={}", userId, e);
+            return R.fail("获取用户积分失败");
+        }
+    }
+    
+    @ApiOperation("更新用户积分")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/update/credit")
+    public R<Boolean> updateUserPoints(SecondUserCreditVo credit) {
+        log.info("更新用户积分,credit={}", credit);
+        try {
+            boolean result = secondUserCreditService.updatePoints(credit);
+            if (result) {
+                return R.success("积分更新成功");
+            } else {
+                return R.fail("积分更新失败");
+            }
+        } catch (Exception e) {
+            log.error("更新用户积分失败,credit={}", credit, e);
+            return R.fail("积分更新失败");
+        }
+    }
+    
+    @ApiOperation("新建用户积分记录")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Integer", paramType = "path", required = true),
+        @ApiImplicitParam(name = "initialPoints", value = "初始积分值", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pointsType", value = "初始积分类型", dataType = "Integer", paramType = "query")
+    })
+    @PostMapping("/create")
+    public void createPointsRecord(@RequestParam("userId")  Integer userId,
+                                   @RequestParam("initialPoints")  Integer initialPoints,
+                                   @RequestParam("pointsType")  Integer pointsType) {
+        log.info("新建用户积分记录,userId={}, initialPoints={}", userId, initialPoints);
+        try {
+            secondUserCreditService.createPointsRecord(userId, initialPoints, pointsType);
+        } catch (Exception e) {
+            log.error("新建用户积分记录失败,userId={}, initialPoints={}", userId, initialPoints, e);
+        }
+    }
+
+    @ApiOperation("用户身份认证成功后增加信用分")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({@ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Integer", required = true)})
+    @GetMapping("/addIdInfoPoints")
+    public R<Boolean> addIdInfoPoints(@RequestParam("userId") Integer userId) throws Exception {
+        log.info("SecondUserPointsController.addIdInfoPoints?userId={}", userId);
+        return R.data(secondUserCreditService.addIdInfoPoints(userId));
+    }
+
+    @ApiOperation("信用分是否达标")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({@ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Integer", required = true)})
+    @GetMapping("/isFree")
+    public R<JSONObject> isFree(@RequestParam("userId") Integer userId) throws Exception {
+        log.info("SecondUserPointsController.isFree?userId={}", userId);
+        return R.data(secondUserCreditService.isFree(userId));
+    }
+}

+ 89 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/SecondUserCreditRecordController.java

@@ -0,0 +1,89 @@
+package shop.alien.second.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondUserCreditRecord;
+import shop.alien.entity.second.vo.SecondUserCreditRecordListVo;
+import shop.alien.second.service.SecondUserCreditRecordService;
+
+import java.util.List;
+
+/**
+ * 二手平台用户积分记录控制器
+ */
+@Slf4j
+@Api(tags = {"二期-用户积分记录"})
+@CrossOrigin
+@RestController
+@RequestMapping("/userPointsRecord")
+@RequiredArgsConstructor
+public class SecondUserCreditRecordController {
+    
+    private final SecondUserCreditRecordService secondUserPointsRecordService;
+    
+    @ApiOperation("获取用户积分记录列表")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/user/{userId}")
+    public R<List<SecondUserCreditRecord>> getUserPointsRecords(@PathVariable Integer userId) {
+        log.info("获取用户积分记录列表,userId={}", userId);
+        try {
+            List<SecondUserCreditRecord> records = secondUserPointsRecordService.getRecordsByUserId(userId);
+            return R.data(records);
+        } catch (Exception e) {
+            log.error("获取用户积分记录列表失败,userId={}", userId, e);
+            return R.fail("获取用户积分记录列表失败");
+        }
+    }
+
+    @ApiOperation("根据用户ID查询用户信用分记录")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "timeRange", value = "时间范围:1-近一周,2-近一个月,3-近一年", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pointsType", value = "积分类型:0-全部,1-正分,2-负分", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getByUserId")
+    public R<SecondUserCreditRecordListVo> getByUserId(
+            @RequestParam Integer userId,
+            @RequestParam(required = false) Integer timeRange,
+            @RequestParam(required = false) Integer pointsType) {
+        return secondUserPointsRecordService.getByUserId(userId, timeRange, pointsType);
+    }
+    
+    @ApiOperation("创建积分记录")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/create")
+    public R<Boolean> createPointsRecord(@RequestBody SecondUserCreditRecord record) {
+        log.info("创建积分记录,userId={}, points={}", record.getUserId(), record.getPoints());
+        try {
+            boolean result = secondUserPointsRecordService.createRecord(record);
+            if (result) {
+                return R.success("积分记录创建成功");
+            } else {
+                return R.fail("积分记录创建失败");
+            }
+        } catch (Exception e) {
+            log.error("创建积分记录失败,record={}", record, e);
+            return R.fail("积分记录创建失败");
+        }
+    }
+
+    @ApiOperation("用户观看完学习视频后增加积分")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "learningId", value = "学习视频ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/addPointsAfterWatchingVideo")
+    public R<String> addPointsAfterWatchingVideo(
+            @RequestParam Integer userId,
+            @RequestParam Integer learningId) {
+        return secondUserPointsRecordService.addPointsAfterWatchingVideo(userId, learningId);
+    }
+}

+ 47 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/TestController.java

@@ -0,0 +1,47 @@
+package shop.alien.second.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.LifeMessageVo;
+import shop.alien.second.feign.AlienStoreFeign;
+import shop.alien.util.common.JwtUtil;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Slf4j
+@Api(tags = {"二手平台-测试"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/testCon")
+@RequiredArgsConstructor
+public class TestController {
+
+    private final AlienStoreFeign alienStoreFeign;
+
+    @GetMapping("/test")
+    public String test() {
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+        }
+        System.out.println(111);
+        System.out.println(data);
+
+        JSONObject iPage = alienStoreFeign.getMessageList("user_13942852153", 1, "");
+        System.out.println(222);
+        System.out.println(iPage);
+
+        return "hello world";
+    }
+
+}

+ 226 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/UserGoodsController.java

@@ -0,0 +1,226 @@
+package shop.alien.second.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.entity.second.vo.SellGoodsVo;
+import shop.alien.second.service.SecondGoodsService;
+import shop.alien.util.common.JwtUtil;
+
+/**
+ * 二手商品控制器
+ */
+@Slf4j
+@Api(tags = {"二手平台-商品管理-我的相关"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/secondGoods")
+@RequiredArgsConstructor
+public class UserGoodsController {
+
+    private final SecondGoodsService secondGoodsService;
+
+    /**
+     * 获取用户屏蔽的商品列表(分页)
+     */
+    @GetMapping("/getShieldedGoodsPage")
+    @ApiOperation("获取用户屏蔽的商品列表(分页)")
+    @ApiImplicitParams({@ApiImplicitParam(name = "pageNum", value = "分页页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "分页条数", dataType = "Integer", paramType = "query", required = true)})
+    public R<IPage<SecondGoodsVo>> getShieldedGoodsPage(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize) {
+        log.info("SecondGoodsController.getShieldedGoodsPage?page={},size={},", pageNum,pageSize);
+        R<IPage<SecondGoodsVo>> result = new R<>();
+        IPage<SecondGoodsVo> page = new Page<>(pageNum, pageSize);
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+            result = R.data(secondGoodsService.getShieldedGoodsPage(page,userId));
+        }
+        return result;
+    }
+
+    /**
+     * 我收藏的商品列表-分页
+     */
+    @GetMapping("/getCollectGoodsPage")
+    @ApiOperation("我收藏的商品列表-分页")
+    @ApiImplicitParams({@ApiImplicitParam(name = "pageNum", value = "分页页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "分页条数", dataType = "Integer", paramType = "query", required = true)})
+    public R<IPage<SecondGoodsVo>> getCollectGoodsPage(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize) {
+        log.info("SecondGoodsController.getCollectGoodsPage?page={},size={},", pageNum,pageSize);
+        R<IPage<SecondGoodsVo>> result = new R<>();
+        IPage<SecondGoodsVo> page = new Page<>(pageNum,pageSize);
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+            result = R.data(secondGoodsService.getCollectGoodsPage(page,userId));
+        }
+        return result;
+    }
+
+    /**
+     * 我的商品-个人主页用户发布的商品
+     */
+    @GetMapping("/getMyGoodsPage")
+    @ApiOperation("我的商品-个人主页用户发布的商品")
+    @ApiImplicitParams({@ApiImplicitParam(name = "pageNum", value = "分页页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "分页条数", dataType = "Integer", paramType = "query", required = true)})
+    public R<IPage<SecondGoodsVo>> getMyGoodsPage(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize) {
+        log.info("SecondGoodsController.getMyGoodsPage?page={},size={},", pageNum,pageSize);
+        R<IPage<SecondGoodsVo>> result = new R<>();
+        IPage<SecondGoodsVo> page = new Page<>(pageNum,pageSize);
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+            result = R.data(secondGoodsService.getMyGoodsPage(page,userId));
+        }
+        return result;
+    }
+
+    /**
+     * 获取商品草稿列表
+     */
+    @GetMapping("/getDraftList")
+    @ApiOperation("获取商品草稿列表")
+    @ApiImplicitParams({@ApiImplicitParam(name = "pageNum", value = "分页页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "分页条数", dataType = "Integer", paramType = "query", required = true)})
+    public R<IPage<SecondGoodsVo>> getDraftList(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize) {
+        log.info("SecondGoodsController.getDraftList?page={},size={},", pageNum,pageSize);
+        R<IPage<SecondGoodsVo>> result = new R<>();
+        IPage<SecondGoodsVo> page = new Page<>(pageNum,pageSize);
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+            result = R.data(secondGoodsService.getDraftList(page,userId));
+        }
+        return result;
+    }
+
+    /**
+     * 获取我的商品草稿数量
+     */
+    @GetMapping("/getDraftCount")
+    @ApiOperation("获取我的商品草稿数量")
+    public R<Integer> getDraftCount() {
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        R<Integer> result = new R<>();
+        // 获取商品草稿数量
+        QueryWrapper<SecondGoods> queryWrapper = new QueryWrapper<>();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+            queryWrapper.lambda()
+                    .eq(SecondGoods::getDeleteFlag, 0)
+                    .eq(SecondGoods::getUserId, userId)
+                    .eq(SecondGoods::getGoodsStatus, 0); // 0-草稿
+            return R.data(secondGoodsService.count(queryWrapper));
+        }
+        return result;
+    }
+
+    /**
+     * 我购买的商品列表-分页
+     */
+    @GetMapping("/getBuyGoodsPage")
+    @ApiOperation("我购买的商品列表-分页")
+    @ApiImplicitParams({@ApiImplicitParam(name = "pageNum", value = "分页页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "分页条数", dataType = "Integer", paramType = "query", required = true)})
+    public R<IPage<SecondGoodsVo>> getBuyGoodsPage(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize) {
+        log.info("SecondGoodsController.getDraftList?page={},size={},", pageNum,pageSize);
+        R<IPage<SecondGoodsVo>> result = new R<>();
+        IPage<SecondGoodsVo> page = new Page<>(pageNum, pageSize);
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+            result = R.data(secondGoodsService.getBuyGoodsPage(page,userId));
+        }
+        return result;
+    }
+
+    /**
+     * 他的商品-个人主页用户发布的商品
+     */
+    @GetMapping("/getUserGoodsPage")
+    @ApiOperation("他的商品-个人主页用户发布的商品")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "主页用户id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentLongitude", value = "经度", dataType = "Double", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentLatitude", value = "纬度", dataType = "Double", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageNum", value = "分页页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "分页条数", dataType = "Integer", paramType = "query", required = true)})
+    public R<IPage<SecondGoodsVo>> getUserGoodsPage(
+            @RequestParam Integer userId,
+            @RequestParam Double currentLongitude,
+            @RequestParam Double currentLatitude,
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize) {
+        log.info("SecondGoodsController.getDraftList?userId={},currentLongitude={},currentLatitude={},page={},size={},",userId,currentLongitude,currentLatitude, pageNum,pageSize);
+        IPage<SecondGoodsVo> page = new Page<>(pageNum, pageSize);
+        return R.data(secondGoodsService.getUserGoodsPage(page,currentLatitude,currentLongitude, userId));
+    }
+
+
+    /**
+     * 我的 - 点赞商品列表
+     */
+    @GetMapping("/getLikeGoodsPage")
+    @ApiOperation("我的 - 点赞商品列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "分页页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "分页条数", dataType = "Integer", paramType = "query", required = true)})
+    public R<IPage<SecondGoodsVo>> getLikeGoodsPage(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize) {
+        log.info("SecondGoodsController.getLikeGoodsPage?page={},size={},", pageNum,pageSize);
+        IPage<SecondGoodsVo> page = new Page<>(pageNum, pageSize);
+        R<IPage<SecondGoodsVo>> result = new R<>();
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            int userId = data.getInteger("userId");
+            String phone = data.getString("phone");
+            result = R.data(secondGoodsService.getLikeGoodsPage(page,userId,phone));
+        }
+        return result;
+    }
+
+    /**
+     * 我的- 交易列表
+     */
+    @GetMapping("/getTransactionList")
+    @ApiOperation("我的- 交易列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "分页页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "分页条数", dataType = "Integer", paramType = "query", required = true)})
+    public R<IPage<SellGoodsVo>> getTransactionList(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize) {
+        log.info("SecondGoodsController.getTransactionList?page={},size={},", pageNum,pageSize);
+        IPage<SellGoodsVo> page = new Page<>(pageNum, pageSize);
+        R<IPage<SellGoodsVo>> result = new R<>();
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        if (null != data) {
+            Integer userId = data.getInteger("userId");
+            result = R.data(secondGoodsService.getTransactionList(page,userId));
+        }
+        return result;
+    }
+
+}

+ 77 - 0
alien-branch-second/src/main/java/shop/alien/second/controller/VideoModerationController.java

@@ -0,0 +1,77 @@
+package shop.alien.second.controller;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.SecondVideoTask;
+import shop.alien.second.service.VideoModerationService;
+
+/**
+ * 视频审核控制器示例
+ */
+@Slf4j
+@RestController
+@RequestMapping("/video/moderation")
+@RequiredArgsConstructor
+public class VideoModerationController {
+    
+    private final VideoModerationService videoModerationService;
+    
+    /**
+     * 提交视频审核任务
+     * 
+     * @param videoUrl 视频URL
+     * @return 任务ID
+     */
+    @PostMapping("/submit")
+    public String submitVideoModerationTask(@RequestParam String videoUrl) {
+        try {
+            String taskId = videoModerationService.submitVideoModerationTask(videoUrl);
+            return "任务提交成功,任务ID: " + taskId;
+        } catch (Exception e) {
+            log.error("提交视频审核任务失败", e);
+            return "任务提交失败: " + e.getMessage();
+        }
+    }
+    
+    /**
+     * 查询视频审核结果
+     * 
+     * @param taskId 任务ID
+     * @return 审核结果
+     */
+    @GetMapping("/result")
+    public Object getVideoModerationResult(@RequestParam String taskId) {
+        try {
+            return videoModerationService.queryVideoModerationResult(taskId);
+        } catch (Exception e) {
+            log.error("查询视频审核结果失败", e);
+            return "查询失败: " + e.getMessage();
+        }
+    }
+
+
+    /**
+     * 处理单个视频审核任务
+     *
+     * @param taskId 任务ID
+     * @return 处理结果
+     */
+    @GetMapping("/processTask")
+    public boolean processTask(@RequestParam("taskId") String taskId) {
+        try {
+            // 根据任务ID查询任务详情
+            SecondVideoTask task = videoModerationService.getTaskByTaskId(taskId);
+            if (task == null) {
+                log.error("未找到任务ID为 {} 的视频审核任务", taskId);
+                return false;
+            }
+
+            // 处理任务
+            return videoModerationService.processTask(task);
+        } catch (Exception e) {
+            log.error("处理视频审核任务时发生异常,任务ID: {}", taskId, e);
+            return false;
+        }
+    }
+}

+ 39 - 0
alien-branch-second/src/main/java/shop/alien/second/exception/GlobalExceptionHandler.java

@@ -0,0 +1,39 @@
+package shop.alien.second.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 全局异常处理器
+ */
+@ControllerAdvice
+public class GlobalExceptionHandler {
+
+    /**
+     * 处理参数校验失败异常
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
+        Map<String, String> errors = new HashMap<>();
+        ex.getBindingResult().getAllErrors().forEach(error -> {
+            String fieldName = ((org.springframework.validation.FieldError) error).getField();
+            String errorMessage = error.getDefaultMessage();
+            errors.put(fieldName, errorMessage);
+        });
+        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
+    }
+
+    /**
+     * 处理其他非法参数异常
+     */
+    @ExceptionHandler(IllegalArgumentException.class)
+    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
+        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
+    }
+}

+ 40 - 0
alien-branch-second/src/main/java/shop/alien/second/feign/AlienStoreFeign.java

@@ -0,0 +1,40 @@
+package shop.alien.second.feign;
+
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@FeignClient(url = "${feign.alienStore.url}", name = "alien-store")
+public interface AlienStoreFeign {
+
+    @GetMapping("/message/getMessageList")
+    JSONObject getMessageList(@RequestParam(value = "receiverId") String receiverId,
+                              @RequestParam(value = "friendType") int friendType,
+                              @RequestParam(value = "search") String search);
+
+//    /**
+//     *
+//     * @param messageReceiverId  消息接收者
+//     * @param senderId           发送人
+//     * @param receiverId         接收人
+//     * @param category           消息类别(message-聊一聊消息  notice-通知)
+//     * @param type               消息类型  1-文本  2-图片 3-链接分享  4-二手交易(确认/拒绝/取消)  5-二手交易签到提醒  6-二手交易已签到
+//     * @param text               消息内容
+//     * @param messageId          消息表存入的主键id
+//     */
+//    @GetMapping("/websocket/sendMsgToClientByPhoneId")
+//    JSONObject sendMsgToClientByPhoneId(@RequestParam(value = "messageReceiverId") String messageReceiverId,
+//                                        @RequestParam(value = "senderId") String senderId,
+//                                        @RequestParam(value = "receiverId") String receiverId,
+//                                        @RequestParam(value = "category") String category,
+//                                        @RequestParam(value = "type") String type,
+//                                        @RequestParam(value = "text") String text,
+//                                        @RequestParam(value = "messageId") Integer messageId);
+
+    @GetMapping("/websocket/sendMsgToClientByPhoneId")
+    JSONObject sendMsgToClientByPhoneId(
+                                        @RequestParam(value = "messageReceiverId") String messageReceiverId,
+                                        @RequestParam(value = "webSocketVoStr") String webSocketVoStr);
+
+}

+ 62 - 0
alien-branch-second/src/main/java/shop/alien/second/platform/PlatformRiskControlController.java

@@ -0,0 +1,62 @@
+package shop.alien.second.platform;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.vo.*;
+import shop.alien.second.service.RiskControlService;
+
+import java.util.List;
+
+/**
+ * 风控商品信息控制器
+ */
+@Slf4j
+@Api(tags = {"二手平台-风控商品信息管理(管理后台)"})
+@ApiSort(2)
+@CrossOrigin
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/riskControl")
+public class PlatformRiskControlController {
+
+    private final RiskControlService riskControlService;
+
+    /**
+     * 风控数据
+     *
+     * @param vo
+     */
+    @ApiOperation("风控数据")
+    @PostMapping("/queryRiskControlRecords")
+    public R<IPage<SecondRiskControlRecordVo>> queryRiskControlRecords(@RequestBody SecondRiskControlRecordVo vo) {
+        return R.data(riskControlService.queryRiskControlRecords(vo));
+    }
+
+
+    @ApiOperation("风控数据")
+    @PostMapping("/queryRiskControlRecordsDetail")
+    public R<SecondRiskRecordVo> queryRiskControlRecordsDetail(@RequestBody SecondRiskRecordVo vo) throws Exception {
+        return R.data(riskControlService.queryRiskControlRecordsDetail(vo));
+    }
+
+    @ApiOperation("账号封禁处理")
+    @PostMapping("/accountBan")
+    public R<SecondRiskRecordVo> accountBan(@RequestBody SecondRiskRecordVo vo) throws Exception {
+        try {
+            boolean type = riskControlService.accountBan(vo);
+            if (!type) {
+                return R.fail("封禁失败");
+            }
+            return R.success("封禁成功");
+        } catch (Exception e) {
+            return R.fail("系统异常,请联系管理员!!!");
+        }
+    }
+
+}

+ 70 - 0
alien-branch-second/src/main/java/shop/alien/second/platform/PlatformSecondCategoryController.java

@@ -0,0 +1,70 @@
+package shop.alien.second.platform;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondGoodsCategory;
+import shop.alien.mapper.second.SecondGoodsCategoryMapper;
+import shop.alien.second.service.SecondGoodsCategoryService;
+
+import java.util.List;
+
+@Slf4j
+@Api(tags = {"二手平台-商品类型"})
+@ApiSort(9)
+@CrossOrigin
+@RestController
+@RequestMapping("/second/goodsCategory")
+@RequiredArgsConstructor
+public class PlatformSecondCategoryController {
+
+    private final SecondGoodsCategoryService service;
+
+    @Autowired
+    private SecondGoodsCategoryMapper mapper;
+
+    @ApiOperation("新增商品类型")
+    @PostMapping("/insertSecondGoodsCategory")
+    public R<String> insertSecondGoodsCategory(@RequestBody SecondGoodsCategory category) throws Exception {
+        log.info("SecondGoodsCategoryController.insertSecondGoodsCategory?category={}", category.toString());
+
+        // 查询商品类型名称
+        QueryWrapper<SecondGoodsCategory> repeatCategory = new QueryWrapper<>();
+        repeatCategory.eq("category_name", category.getCategoryName().trim());
+        List<SecondGoodsCategory> list = mapper.selectList(repeatCategory);
+        if (list.size() > 0) {
+            return R.fail("商品类型名称已存在,请修改商品类型名称!");
+        }
+
+        Integer reporting = service.insertSecondGoodsCategory(category);
+        if (0 == reporting) {
+            return R.fail("新增商品类型失败");
+        } else {
+            return R.data("新增商品类型成功");
+        }
+    }
+
+    @ApiOperation("修改商品类型")
+    @PostMapping("/updateSecondGoodsCategory")
+    public R<String> updateSecondGoodsCategory(@RequestBody SecondGoodsCategory category) throws Exception  {
+        log.info("SecondGoodsCategoryController.updateSecondGoodsCategory?category={}", category.toString());
+        Integer reporting = service.updateSecondGoodsCategory(category);
+        if (0 == reporting) {
+            return R.fail("修改商品类型失败");
+        } else {
+            return R.data("修改商品类型成功");
+        }
+    }
+
+    @ApiOperation("搜索商品类型树形结构")
+    @PostMapping("/querySecondGoodsTree")
+    public R<List<SecondGoodsCategory>> querySecondGoodsTree() throws Exception {
+        return R.data(service.querySecondGoodsTree(), "查询成功");
+    }
+}

+ 85 - 0
alien-branch-second/src/main/java/shop/alien/second/platform/PlatformSecondTradeController.java

@@ -0,0 +1,85 @@
+package shop.alien.second.platform;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.vo.SecondTradeRecordVo;
+import shop.alien.second.service.PlatformSecondTradeService;
+
+import java.util.Date;
+import java.util.List;
+
+@Slf4j
+@Api(tags = {"平台-二手交易记录"})
+@ApiSort(1)
+@CrossOrigin
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/platformSecondTrade")
+public class PlatformSecondTradeController {
+
+    private final PlatformSecondTradeService secondTradeRecordService;
+
+    @ApiOperation("交易列表(分页)")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/getTradeRecordPage")
+    public R<IPage<SecondTradeRecordVo>> getTradeRecordPage(@RequestBody SecondTradeRecordVo vo) throws Exception {
+        log.info("PlatformSecondTradeController.getTradeRecordPage?vo={}", vo.toString());
+        return R.data(secondTradeRecordService.getTradeRecordPage(vo));
+    }
+
+    @ApiOperation("交易列表(不分页)")
+    @ApiOperationSupport(order = 2)
+    @GetMapping("/getTradeRecordList")
+    public R<List<SecondTradeRecordVo>> getTradeRecordList(Integer userId, String beginTime, String endTime) throws Exception {
+        log.info("PlatformSecondTradeController.getTradeRecordList?userId={},beginTime={},endTime={}", userId, beginTime, endTime);
+        return R.data(secondTradeRecordService.getTradeRecordList(userId, beginTime, endTime));
+    }
+
+    @ApiOperation("交易详情")
+    @ApiOperationSupport(order = 3)
+    @GetMapping("/getTradeRecordById")
+    public R<SecondTradeRecordVo> getTradeRecordById(Integer id) throws Exception {
+        log.info("PlatformSecondTradeController.getTradeRecordById?id={}",id);
+        return R.data(secondTradeRecordService.getTradeRecordById(id));
+    }
+
+    @ApiOperation("排行榜")
+    @ApiOperationSupport(order = 4)
+    @GetMapping("/getRankingList")
+    public R<List<JSONObject>> getRankingList(String beginTime, String endTime) throws Exception {
+        log.info("PlatformSecondTradeController.getRankingList");
+        return R.data(secondTradeRecordService.getRankingList(beginTime, endTime));
+    }
+
+    @ApiOperation("交易失效")
+    @ApiOperationSupport(order = 5)
+    @GetMapping("/setTradeFailure")
+    public R<Boolean> setTradeFailure(Integer tradeId, String failureReason) throws Exception {
+        log.info("PlatformSecondTradeController.setTradeFailure?tradeId={},failureReason={}", tradeId, failureReason);
+        secondTradeRecordService.setTradeFailure(tradeId, failureReason);
+        return R.success("操作成功");
+    }
+
+    @ApiOperation("查询存在洗钱嫌疑的交易")
+    @ApiOperationSupport(order = 6)
+    @GetMapping("/getTradeRecordListByRiskId")
+    public R<List<SecondTradeRecordVo>> getTradeRecordListByRiskId(Integer id) throws Exception {
+        log.info("PlatformSecondTradeController.getTradeRecordListByRiskId?id={}", id);
+        return R.data(secondTradeRecordService.getTradeRecordListByRiskId(id));
+    }
+
+    @ApiOperation("封禁账号取消交易")
+    @ApiOperationSupport(order = 7)
+    @GetMapping("/bannedAccountCancelTrade")
+    public R<Boolean> bannedAccountCancelTrade(Integer userId) throws Exception {
+        log.info("PlatformSecondTradeController.bannedAccountCancelTrade?userId={}", userId);
+        secondTradeRecordService.bannedAccountCancelTrade(userId);
+        return R.success("操作成功");
+    }
+
+}

+ 39 - 0
alien-branch-second/src/main/java/shop/alien/second/platform/PlatformSecondUserFreezeController.java

@@ -0,0 +1,39 @@
+package shop.alien.second.platform;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiOperationSupport;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.vo.SecondTradeRecordVo;
+import shop.alien.second.service.PlatformSecondTradeService;
+
+import java.util.List;
+
+@Slf4j
+@Api(tags = {"平台-账号冻结"})
+@ApiSort(1)
+@CrossOrigin
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/userFreeze")
+public class PlatformSecondUserFreezeController {
+
+    private final PlatformSecondTradeService secondTradeRecordService;
+
+    @ApiOperation("交易列表(分页)")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/getTradeRecordPage")
+    public R<IPage<SecondTradeRecordVo>> getTradeRecordPage(@RequestBody SecondTradeRecordVo vo) throws Exception {
+        log.info("PlatformSecondTradeController.getTradeRecordPage?vo={}", vo.toString());
+        return R.data(secondTradeRecordService.getTradeRecordPage(vo));
+    }
+
+
+
+}

+ 57 - 0
alien-branch-second/src/main/java/shop/alien/second/platform/PlatformUserViolationController.java

@@ -0,0 +1,57 @@
+package shop.alien.second.platform;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.vo.SecondUserViolationDetailVo;
+import shop.alien.entity.second.vo.SecondUserViolationVo;
+import shop.alien.entity.store.LifeUserViolation;
+import shop.alien.second.service.PlatformUserViolationService;
+
+/**
+ * 作者:邹建宇
+ * 描述:
+ */
+@Slf4j
+@Api(tags = {"平台-二手举报管理"})
+@ApiSort(1)
+@CrossOrigin
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/platformUserViolation")
+public class PlatformUserViolationController {
+
+    private final PlatformUserViolationService service;
+
+    @ApiOperation("查询举报信息")
+    @PostMapping("/queryUserViolationByPage")
+    public R<IPage<SecondUserViolationVo>> queryUserViolationByPage(@RequestBody SecondUserViolationVo vo) throws Exception {
+        log.info("PlatformUserViolationController.queryUserViolationByPage");
+        IPage<SecondUserViolationVo> page = new Page<>(vo.getPageNum(), vo.getPageSize());
+        return R.data(service.getUserViolationByPage(page, vo.getReportingUserName(), vo.getReportingDate(), vo.getProcessingStatus(), vo.getReportContextType()), "查询成功");
+    }
+
+
+    @ApiOperation("查询举报详情")
+    @GetMapping("/queryUserViolationDetail")
+    public R<SecondUserViolationDetailVo> queryUserViolationDetail(Integer id) throws Exception {
+        log.info("PlatformUserViolationController.queryUserViolationDetail");
+        return R.data(service.getUserViolationDetail(id), "查询成功");
+    }
+
+    @ApiOperation("举报审核")
+    @PostMapping("/updateUserViolation")
+    public R<String> updateUserViolation(@RequestBody LifeUserViolation row) throws Exception {
+        log.info("PlatformUserViolationController.queryUserViolationDetail");
+        int num = service.updateUserViolation(row);
+        if(num > 0) return R.success("审核成功");
+        return R.fail("审核失败");
+    }
+
+}

+ 30 - 0
alien-branch-second/src/main/java/shop/alien/second/service/PlatformSecondTradeService.java

@@ -0,0 +1,30 @@
+package shop.alien.second.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.context.annotation.Lazy;
+import shop.alien.entity.second.SecondTradeRecord;
+import shop.alien.entity.second.vo.SecondTradeRecordVo;
+
+import java.util.Date;
+import java.util.List;
+
+public interface PlatformSecondTradeService extends IService<SecondTradeRecord> {
+
+    IPage<SecondTradeRecordVo> getTradeRecordPage(SecondTradeRecordVo vo) throws Exception;
+
+    List<SecondTradeRecordVo> getTradeRecordList(Integer userId, String beginTime, String endTime) throws Exception;
+
+    SecondTradeRecordVo getTradeRecordById(Integer id) throws Exception;
+
+    List<JSONObject> getOperationJsonList(Integer tradeId) throws Exception;
+
+    List<JSONObject> getRankingList(String beginTime, String endTime) throws Exception;
+
+    void setTradeFailure(Integer tradeId, String failureReason) throws Exception;
+
+    List<SecondTradeRecordVo> getTradeRecordListByRiskId(Integer id) throws Exception;
+
+    void bannedAccountCancelTrade(Integer userId) throws Exception;
+}

+ 16 - 0
alien-branch-second/src/main/java/shop/alien/second/service/PlatformUserViolationService.java

@@ -0,0 +1,16 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.second.vo.SecondUserViolationDetailVo;
+import shop.alien.entity.second.vo.SecondUserViolationVo;
+import shop.alien.entity.store.LifeUserViolation;
+
+public interface PlatformUserViolationService extends IService<SecondUserViolationVo> {
+
+    IPage<SecondUserViolationVo> getUserViolationByPage(IPage<SecondUserViolationVo> page, String reportingUserName, String reportingDate, String processingStatus, String reportContextType) throws Exception;
+
+    SecondUserViolationDetailVo getUserViolationDetail(Integer id) throws Exception;
+
+    int updateUserViolation(LifeUserViolation row) throws Exception;
+}

+ 26 - 0
alien-branch-second/src/main/java/shop/alien/second/service/RiskControlGoodsService.java

@@ -0,0 +1,26 @@
+package shop.alien.second.service;
+
+import shop.alien.entity.second.vo.SecondGoodsRecordDetailVo;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+
+import java.util.List;
+
+/**
+ * 风控商品服务接口
+ */
+public interface RiskControlGoodsService {
+
+    /**
+     * 通过商品记录表ID批量查询商品信息(包含图片)
+     * @param recordIds 商品记录表ID列表
+     * @return 商品信息列表
+     */
+    List<SecondGoodsRecordDetailVo> getGoodsInfoByRecordIds(List<Integer> recordIds);
+
+    /**
+     * 通过商品表ID批量查询商品信息(包含图片)
+     * @param goodsIds 商品表ID列表
+     * @return 商品信息列表
+     */
+    List<SecondGoodsVo> getGoodsInfoByGoodsIds(List<Integer> goodsIds);
+}

+ 63 - 0
alien-branch-second/src/main/java/shop/alien/second/service/RiskControlService.java

@@ -0,0 +1,63 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.second.SecondRiskControlRecord;
+import shop.alien.entity.second.vo.SecondRiskControlRecordVo;
+import shop.alien.entity.second.vo.SecondRiskRecordVo;
+
+import java.util.List;
+
+/**
+ * 风控服务接口
+ */
+public interface RiskControlService extends IService<SecondRiskControlRecord> {
+    
+    /**
+     * 记录风控数据
+     *
+     * @param userId     用户ID
+     * @param ruleType   规则类型 1:洗钱嫌疑 2:账号异常 3:交易欺诈 4:异常发布
+     * @param ruleName   规则名称
+     * @param businessId 业务ID
+     * @param detailInfo 详细信息(JSON格式)
+     */
+    void recordRiskControlData(Integer userId,
+                               Integer ruleType,
+                               String ruleName,
+                               String businessId,
+                               String detailInfo);
+
+    /**
+     * 获取相同类型的风控数据
+     *
+     * @param ruleType   规则类型
+     * @param businessId 业务ID
+     * @return 相同类型的风控数据列表
+     */
+    List<SecondRiskControlRecord> getSameTypeRiskControlRecords(Integer ruleType, String businessId);
+
+    /**
+     * 发送封禁通知
+     */
+    void sendNotice(Integer userId);
+
+    /**
+     * 获取相同类型的风控数据
+     *
+     * @param vo
+     * @return 查询风控列表
+     */
+    IPage<SecondRiskControlRecordVo> queryRiskControlRecords(SecondRiskControlRecordVo vo);
+
+    /**
+     * 账号封禁详细查询
+     */
+    SecondRiskRecordVo queryRiskControlRecordsDetail(SecondRiskRecordVo vo) throws Exception;
+
+    /**
+     * 账号封禁处理
+     */
+    boolean accountBan(SecondRiskRecordVo vo) throws Exception;
+
+}

+ 90 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondEntrustUserService.java

@@ -0,0 +1,90 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.second.SecondEntrustUser;
+import shop.alien.entity.second.vo.SecondEntrustUserDTO;
+import shop.alien.entity.second.vo.SecondEntrustUserDetailVo;
+import shop.alien.entity.second.vo.SecondEntrustUserQueryVo;
+import shop.alien.entity.second.vo.SecondEntrustUserResultVo;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 二手委托人信息表 服务类
+ * </p>
+ *
+ * @author ssk
+ * @since 2025-11-21
+ */
+public interface SecondEntrustUserService extends IService<SecondEntrustUser> {
+
+    /**
+     * 创建委托人信息
+     *
+     * @param dto 委托人信息DTO
+     * @return 是否创建成功
+     */
+    boolean createEntrustUser(SecondEntrustUserDTO dto);
+
+    /**
+     * 根据交易ID获取委托人信息
+     *
+     * @param entrustTradeId 交易ID
+     * @return 委托人信息
+     */
+    SecondEntrustUser getByTradeId(Integer entrustTradeId);
+
+    /**
+     * 根据交易编号获取委托人信息
+     *
+     * @param entrustTradeNo 交易编号
+     * @return 委托人信息
+     */
+    SecondEntrustUser getByTradeNo(String entrustTradeNo);
+
+    /**
+     * 更新委托人信息
+     *
+     * @param id 委托人ID
+     * @param dto 委托人信息DTO
+     * @return 是否更新成功
+     */
+    boolean updateEntrustUser(Integer id, SecondEntrustUserDTO dto);
+
+    /**
+     * 删除委托人信息
+     *
+     * @param id 委托人ID
+     * @return 是否删除成功
+     */
+    boolean deleteEntrustUser(Integer id);
+
+    /**
+     * 根据用户电话查询委托人信息列表
+     *
+     * @param entrustUserPhone 用户电话
+     * @return 委托人信息列表
+     */
+    List<SecondEntrustUser> getByUserPhone(String entrustUserPhone);
+
+    /**
+     * 分页查询委托人信息列表(以姓名+身份证号为维度)
+     *
+     * @param queryVo 查询条件
+     * @return 委托人信息分页列表
+     */
+    IPage<SecondEntrustUserResultVo> getEntrustUserPage(SecondEntrustUserQueryVo queryVo);
+
+    /**
+     * 根据姓名和身份证号获取委托人详情(包含该人所有委托记录的交易信息)
+     *
+     * @param entrustUserName 委托人姓名
+     * @param entrustIdCard 委托人身份证号
+     * @return 委托人详情
+     */
+    SecondEntrustUserDetailVo getEntrustUserDetail(String entrustUserName, String entrustIdCard) throws Exception;
+
+}
+

+ 98 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsAuditService.java

@@ -0,0 +1,98 @@
+package shop.alien.second.service;
+
+import shop.alien.entity.SecondVideoTask;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+
+import java.util.List;
+
+/**
+ * 二手商品审核服务接口
+ * 负责商品的图片、文本、视频审核相关业务逻辑
+ */
+public interface SecondGoodsAuditService {
+
+    /**
+     * 执行内容审核(图片、文本和视频)
+     * @param goodsDTO 商品信息DTO
+     * @param goods 商品实体
+     * @throws Exception 审核过程中可能抛出的异常
+     */
+    void performContentReview(SecondGoodsVo goodsDTO, SecondGoods goods) throws Exception;
+
+    /**
+     * 执行图片审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果,true表示通过,false表示不通过
+     * @throws Exception 审核过程中可能抛出的异常
+     */
+    boolean performImageReviews(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception;
+
+    /**
+     * 执行文本审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果,true表示通过,false表示不通过
+     * @throws Exception 审核过程中可能抛出的异常
+     */
+    boolean performTextReview(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception;
+
+    /**
+     * 执行视频审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 视频审核任务ID列表
+     */
+    List<String> performVideoReviews(SecondGoods goods, SecondGoodsVo goodsDTO);
+
+    /**
+     * 处理视频审核结果
+     * @param task 视频审核任务
+     */
+    void processVideoModerationResult(SecondVideoTask task);
+
+    /**
+     * 审核通过后上架商品
+     * @param goods 商品信息
+     */
+    void approveAndListGoods(SecondGoods goods);
+
+    /**
+     * 创建商品审核记录
+     * @param goods 商品信息
+     * @param failReason 审核失败原因
+     * @param goodsStatus 商品状态
+     */
+    void createGoodsAudit(SecondGoods goods, String failReason, Integer goodsStatus);
+
+    /**
+     * 从图片URL列表中提取视频URL
+     * @param imageUrls 图片URL列表
+     * @return 视频URL列表
+     */
+    List<String> extractVideoUrls(List<String> imageUrls);
+
+    /**
+     * 判断URL是否为视频地址
+     * @param url 输入URL
+     * @return 是否为视频地址
+     */
+    boolean isVideoUrl(String url);
+
+    boolean performSecondRoundReview(SecondGoods goods, SecondGoodsVo goodsDTO);
+
+    /**
+     * 获取AI商品审核结果
+     * 查询所有状态为处理中的AI审核任务,并更新审核结果
+     * @return 处理结果
+     */
+    String getAiGoodsCheckResult();
+
+    /**
+     * 商品审核通过后的上架处理(包含风控检查)
+     * @param goods 商品信息
+     */
+    void handleGoodsApprovalSuccess(SecondGoods goods);
+}
+

+ 26 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsCategoryService.java

@@ -0,0 +1,26 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.second.SecondGoodsCategory;
+
+import java.util.List;
+
+/**
+ * 二手商品服务接口
+ */
+public interface SecondGoodsCategoryService extends IService<SecondGoodsCategory> {
+
+    /**
+     * 获取分类列表
+     * @param parentId 父ID
+     * @return 分页后的屏蔽商品列
+     */
+    List<SecondGoodsCategory> querySecondGoodsByParentId(Integer parentId, Integer categoryState) throws Exception;
+
+    Integer insertSecondGoodsCategory(SecondGoodsCategory category) throws Exception;
+
+    Integer updateSecondGoodsCategory(SecondGoodsCategory category) throws Exception;
+
+    List<SecondGoodsCategory> querySecondGoodsTree() throws Exception;
+
+}

+ 29 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsNotificationService.java

@@ -0,0 +1,29 @@
+package shop.alien.second.service;
+
+import shop.alien.entity.second.SecondGoods;
+
+/**
+ * 二手商品消息通知服务接口
+ * 负责商品相关的消息通知业务逻辑
+ */
+public interface SecondGoodsNotificationService {
+
+    /**
+     * 发送审核成功消息
+     * @param goods 商品信息
+     */
+    void sendMessage(SecondGoods goods);
+
+    /**
+     * 发送审核失败消息
+     * @param goods 商品信息
+     */
+    void sendFailedMsg(SecondGoods goods);
+
+    /**
+     * 发送商品下架消息
+     * @param goods 商品信息
+     */
+    void sendShelveMessage(SecondGoods goods);
+}
+

+ 18 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsOperationRecordService.java

@@ -0,0 +1,18 @@
+package shop.alien.second.service;
+
+import shop.alien.entity.second.SecondGoods;
+
+/**
+ * 二手商品操作历史记录服务接口
+ * 负责记录商品的各种操作历史
+ */
+public interface SecondGoodsOperationRecordService {
+
+    /**
+     * 记录商品操作历史
+     * @param goods 商品信息
+     * @param operationName 操作名称
+     */
+    void recordGoodsOperation(SecondGoods goods, String operationName);
+}
+

+ 25 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsReportingService.java

@@ -0,0 +1,25 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.second.vo.SecondReportingVo;
+import shop.alien.entity.second.vo.SecondUserViolationVo;
+
+
+/**
+ * 二手商品服务接口
+ */
+public interface SecondGoodsReportingService {
+
+
+    /**
+     * 查询商品详情信息
+     * @param id ID
+     * @return 举报详情
+     */
+    SecondReportingVo queryReportingDetail(Integer id);
+
+
+    int reporting(SecondUserViolationVo lifeuserViolation) throws Exception;
+
+
+}

+ 226 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondGoodsService.java

@@ -0,0 +1,226 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.SecondVideoTask;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.SecondGoodsRecord;
+import shop.alien.entity.second.SecondRiskControlRecord;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.entity.second.vo.SecondGoodsAdminQueryDTO;
+import shop.alien.entity.second.vo.SecondGoodsDetailVo;
+import shop.alien.entity.second.vo.SecondGoodsRecordDetailVo;
+import shop.alien.entity.second.vo.SellGoodsVo;
+
+import java.util.List;
+
+/**
+ * 二手商品服务接口
+ */
+public interface SecondGoodsService extends IService<SecondGoods> {
+
+    /**
+     * 创建二手商品基本信息
+     * @param goods 二手商品实体
+     * @param editFlag 编辑标识 0:创建 1:编辑
+     * @return 是否成功保存
+     */
+    @Transactional
+    boolean createBasicInfo(SecondGoodsVo goods, Integer editFlag) throws Exception;
+
+    /**
+     * 保存为草稿
+     * @param goods 二手商品实体
+     * @return 是否成功保存
+     */
+    boolean saveAsDraft(SecondGoodsVo goods);
+
+    /**
+     * 获取用户屏蔽的商品列表(分页)
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @param shieldType 屏蔽类型
+     * @return 分页后的屏蔽商品列表
+     */
+    IPage<SecondGoods> getShieldedGoodsListByType(IPage<SecondGoods> page, Integer userId, Integer shieldType);
+
+    /**
+     * 获取商品热卖排行榜
+     * @param page 分页参数
+     * @return 商品热卖排行榜列表
+     */
+    IPage<SecondGoods> getHotSellingRanking(IPage<SecondGoods> page);
+
+    /**
+     * 获取商品热卖排行榜(前10名),按标签聚合计算总点赞量
+     * @return 热卖排行榜列表
+     */
+    List<SecondGoods> getHotSellingRankingTop10();
+
+    /**
+     * 获取商品收藏排行榜(前10名)
+     * @return 商品收藏排行榜列表
+     */
+    List<SecondGoods> getCollectTop10();
+
+    /**
+     * 搜索结果-商品列表
+     * @param secondGoodsVo 搜索条件
+     * @return 搜索结果列表
+     */
+    IPage<SecondGoodsVo> searchGoodsList(IPage<SecondGoodsVo> page,Integer userId, SecondGoodsVo secondGoodsVo);
+
+    /**
+     * 获取商品详情
+     * @param id 商品ID
+     * @return 商品详情
+     */
+    SecondGoodsVo getSecondGoodsById(Integer id);
+
+    /**
+     * 获取用户屏蔽的商品列表(分页)
+     * @param page 搜索条件
+     * @param userId 用户ID
+     * @return 分页后的屏蔽商品列表
+     */
+    IPage<SecondGoodsVo>  getShieldedGoodsPage(IPage<SecondGoodsVo> page, Integer userId);
+
+    /**
+     * 获取我收藏的商品列表(分页)
+     * @param page 搜索条件
+     * @param userId 用户ID
+     * @return 分页后的收藏商品列表
+     */
+    IPage<SecondGoodsVo> getCollectGoodsPage(IPage<SecondGoodsVo> page, int userId);
+
+    /**
+     * 获取我购买的商品列表(分页)
+     * @param page 搜索条件
+     * @param userId 用户ID
+     * @return 分页后的购买商品列表
+     */
+    IPage<SecondGoodsVo> getBuyGoodsPage(IPage<SecondGoodsVo> page, int userId);
+
+    /**
+     * 获取我卖出的商品列表(分页)
+     * @param page 搜索条件
+     * @param secondGoodsVo 查询参数
+     * @param userId 用户ID
+     * @return 分页后的卖出商品列表
+     */
+    IPage<SecondGoodsVo> getSellGoodsPage(IPage<SecondGoodsVo> page, SecondGoodsVo secondGoodsVo,int userId);
+
+    /**
+     * 获取用户发布的商品列表(分页)
+     * @param page 搜索条件
+     * @param currentLatitude 当前纬度
+     * @param currentLongitude 当前经度
+     * @param userId 用户ID
+     * @return 分页后的用户商品列表
+     */
+    IPage<SecondGoodsVo> getUserGoodsPage(IPage<SecondGoodsVo> page, Double currentLatitude , Double currentLongitude, Integer userId);
+
+    /**
+     * 获取我的商品列表(分页)
+     * @param page 搜索条件
+     * @param userId 用户ID
+     * @return 分页后的我的商品列表
+     */
+    IPage<SecondGoodsVo> getMyGoodsPage(IPage<SecondGoodsVo> page, int userId);
+
+    /**
+     * 获取草稿列表(分页)
+     * @param page 搜索条件
+     * @param userId 用户ID
+     * @return 分页后的草稿列表
+     */
+    IPage<SecondGoodsVo> getDraftList(IPage<SecondGoodsVo> page, int userId);
+
+    /**
+     * 获取点赞商品列表(分页)
+     * @param page 搜索条件
+     * @param userId 用户ID
+     * @param phone 用户手机号
+     * @return 分页后的点赞商品列表
+     */
+    IPage<SecondGoodsVo> getLikeGoodsPage(IPage<SecondGoodsVo> page, int userId, String phone);
+
+    /**
+     * 获取商品交易列表(分页)
+     * @param page 搜索条件
+     * @param userId 用户ID
+     * @return 分页后的商品交易列表
+     */
+    IPage<SellGoodsVo> getTransactionList(IPage<SellGoodsVo> page, Integer userId);
+
+    List<SecondGoods> getGoodsListByUserId(Integer userId, Integer goodsStatus);
+
+    /**
+     * 处理视频审核结果
+     * @param task 视频审核任务
+     */
+    void processVideoModerationResult(SecondVideoTask task);
+    
+    /**
+     * 记录商品操作历史
+     * @param goods 商品信息
+     */
+    void recordGoodsOperation(SecondGoods goods,String operationName);
+    /**
+     * 管理后台商品列表查询
+     * @param page 分页参数
+     * @param queryDTO 查询参数
+     * @return 商品列表
+     */
+    IPage<SecondGoodsVo> getAdminGoodsList(IPage<SecondGoodsVo> page, SecondGoodsAdminQueryDTO queryDTO);
+    
+    /**
+     * 获取商品详情(管理后台使用)
+     * @param goodsId 商品ID
+     * @return 商品详情信息
+     */
+    SecondGoodsDetailVo getAdminGoodsDetail(Integer goodsId) throws Exception;
+
+    /**
+     * 获取管理后台商品操作记录详情
+     * @param recordId 操作记录ID
+     * @return 操作记录详情
+     */
+    SecondGoodsRecordDetailVo getAdminGoodsRecordDetail(Integer recordId);
+
+    /**
+     * 下架商品
+     * @param secondGoods 商品信息
+     * @return 是否下架成功
+     */
+    boolean shelveSecondGoods(SecondGoodsVo secondGoods);
+
+    /**
+     * 处理商品信息(管理后台使用)
+     * @param goodsId 商品ID
+     * @return 处理后的商品详情信息
+     */
+    SecondGoodsDetailVo dealSecondGoodsInfo(Integer goodsId);
+
+    /**
+     * 处理商品记录信息(管理后台使用)
+     * @param goodsId 商品记录ID
+     * @return 处理后的商品详情信息
+     */
+    SecondGoodsVo dealSecondGoodsRecordInfo(Integer goodsId);
+
+    /**
+     * 批量处理商品信息(管理后台使用)( ruleType = 4 异常发布场景)
+     * @param ruleType 风险类型
+     * @param businessId 业务ID
+     * @return 是否处理成功
+     */
+    boolean batchShelveGoodsByRiskControlRecord(Integer ruleType, String businessId);
+
+    /**
+     * 执行商品发布风控检查
+     * @param goods 商品信息
+     */
+    void performPublishRiskCheck(SecondGoods goods);
+}

+ 48 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondRecommendService.java

@@ -0,0 +1,48 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.second.vo.SecondGoodsRecommendVo;
+
+
+/**
+ * 二手商品服务接口
+ */
+public interface SecondRecommendService extends IService<SecondGoodsRecommendVo> {
+
+    /**
+     * 查询推荐信息
+     * @param page 分页信息
+     * @param longitude 经度信息
+     * @param latitude 维度信息
+     * @param typeId 类型ID
+     * @return 推荐商品信息
+     */
+    IPage<SecondGoodsRecommendVo> getSecondRecommendByPage(IPage<SecondGoodsRecommendVo> page, String longitude, String latitude, Integer typeId, String radiusKm) throws Exception;
+
+    /**
+     * 查询关注信息
+     * @param page 分页信息
+     * @param position 经纬度
+     * @return 关注商品信息
+     */
+    IPage<SecondGoodsRecommendVo> querySecondConcernByPage(IPage<SecondGoodsRecommendVo> page, String position) throws Exception;
+
+    /**
+     * 查询新品信息
+     * @param page 分页信息
+     * @param position 经纬度
+     * @return 新品商品信息
+     */
+    IPage<SecondGoodsRecommendVo> querySecondNewGoodsByPage(IPage<SecondGoodsRecommendVo> page, String position, String radiusKm) throws Exception;
+
+    /**
+     * 查询商品详情信息
+     * @param goodsId 商品ID
+     * @param position 经纬度
+     * @return 商品详情信息
+     */
+    SecondGoodsRecommendVo querySecondGoodsDetail(Integer goodsId, String position) throws Exception;
+
+
+}

+ 61 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondTradeRecordService.java

@@ -0,0 +1,61 @@
+package shop.alien.second.service;
+
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.BusinessException;
+import shop.alien.entity.second.SecondTradeRecord;
+import shop.alien.entity.second.vo.SecondEntrustUserDTO;
+import shop.alien.entity.second.vo.SecondTradeRecordVo;
+import shop.alien.entity.second.vo.SellerEvaluationVo;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 二手交易记录表 服务类
+ * </p>
+ *
+ * @author qrs
+ * @since 2025-07-07
+ */
+public interface SecondTradeRecordService extends IService<SecondTradeRecord> {
+    boolean createTrade(SecondEntrustUserDTO entity) throws Exception;
+
+    boolean goodsTradeConfirm(int goodsId) throws Exception;
+
+    boolean tradeConfirm(int tradeId, int messageId, int type) throws BusinessException, Exception;
+
+    boolean cancelTrade(int tradeId, String cancelReason, String cancelReasonSupplement) throws Exception;
+
+    boolean whetherCanSignIn(int tradeId) throws Exception;
+
+    boolean tradeSignIn(int tradeId, int messageId, String signInLatitudeLongitude, String signInLatitudeLongitudeAddress) throws BusinessException, Exception;
+
+    SecondTradeRecordVo getUserTradeStatus(int tradeId) throws Exception;
+
+    boolean tradeCompleteConfirm(int tradeId, int type, String evaluate, Integer rating) throws Exception;
+
+    List<SecondTradeRecordVo> getTradeRecord(int sideId) throws Exception;
+
+    SecondTradeRecord hasInTradeRecord(Integer sideId) throws Exception;
+
+    boolean modifyTradeRecord(int type, Integer tradeId, String transactionTime, String transactionLatitudeLongitude, String transactionLatitudeLongitudeAddress, String transactionLocation, String messageId,
+                              String userPhone, String userName, String idCard, Integer entrustId, String idCardImg) throws Exception;
+
+    /**
+     * 获取用户作为卖家的交易评价列表(分页)
+     *
+     * @param sellerId 卖家ID(用户ID,必传)
+     * @param pageNum 页码
+     * @param pageSize 每页条数
+     * @return 卖家评价分页列表,包含买家信息、评价内容、评分、风控评分等级等
+     */
+    IPage<SellerEvaluationVo> getSellerEvaluationList(Integer sellerId, Integer pageNum, Integer pageSize) throws Exception;
+
+    void locationShareAdd(Integer otherUserId) throws Exception;
+
+    void locationShareDel(Integer otherUserId) throws Exception;
+
+    boolean locationShareHas(Integer otherUserId) throws Exception;
+}

+ 41 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondUserCreditRecordService.java

@@ -0,0 +1,41 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondUserCredit;
+import shop.alien.entity.second.SecondUserCreditRecord;
+import shop.alien.entity.second.vo.SecondUserCreditRecordListVo;
+
+import java.util.List;
+
+/**
+ * 二手平台用户积分记录服务接口
+ */
+public interface SecondUserCreditRecordService extends IService<SecondUserCreditRecord> {
+    
+    /**
+     * 根据用户ID获取积分记录列表
+     * @param userId 用户ID
+     * @return 积分记录列表
+     */
+    List<SecondUserCreditRecord> getRecordsByUserId(Integer userId);
+    
+    /**
+     * 创建积分记录
+     * @param record 积分记录信息
+     * @return 是否创建成功
+     */
+    boolean createRecord(SecondUserCreditRecord record);
+
+    R<SecondUserCreditRecordListVo> getByUserId(Integer userId, Integer timeRange, Integer pointsType);
+
+    /**
+     * 用户观看完学习视频后增加积分
+     * @param userId 用户ID
+     * @param learningId 学习视频ID
+     * @return 操作结果
+     */
+    R<String> addPointsAfterWatchingVideo(Integer userId, Integer learningId);
+
+
+}

+ 38 - 0
alien-branch-second/src/main/java/shop/alien/second/service/SecondUserCreditService.java

@@ -0,0 +1,38 @@
+package shop.alien.second.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.second.SecondUserCredit;
+import shop.alien.entity.second.vo.SecondUserCreditVo;
+
+/**
+ * 二手平台用户积分服务接口
+ */
+public interface SecondUserCreditService extends IService<SecondUserCredit> {
+    
+    /**
+     * 根据用户ID获取用户积分信息
+     * @param userId 用户ID
+     * @return 用户积分信息
+     */
+    SecondUserCredit getByUserId(Integer userId);
+    
+    /**
+     * 更新用户积分
+     * @param credit
+     * @return 是否更新成功
+     */
+    boolean updatePoints(SecondUserCreditVo credit);
+    
+    /**
+     * 新建用户积分记录
+     * @param userId 用户ID
+     * @param initialPoints 初始积分值
+     * @return 是否创建成功
+     */
+    boolean createPointsRecord(Integer userId, Integer initialPoints, Integer pointsType);
+
+    boolean addIdInfoPoints(Integer userId) throws Exception;
+
+    JSONObject isFree(Integer userId) throws Exception;
+}

+ 41 - 0
alien-branch-second/src/main/java/shop/alien/second/service/VideoModerationService.java

@@ -0,0 +1,41 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.SecondVideoTask;
+
+public interface VideoModerationService  extends IService<SecondVideoTask> {
+    /**
+     * 提交视频审核任务
+     *
+     * @param videoUrl 视频URL
+     * @return 任务ID
+     */
+    String submitVideoModerationTask(String videoUrl);
+    /**
+     * 查询视频审核结果
+     *
+     * @param taskId 任务ID
+     * @return 审核结果
+     */
+    SecondVideoTask queryVideoModerationResult(String taskId);
+    /**
+     * 处理视频审核结果
+     *
+     * @param task 视频审核任务
+     * @return 是否处理成功
+     */
+    boolean processTask(SecondVideoTask task);
+    /**
+     * 根据任务ID获取视频审核任务
+     *
+     * @param taskId 任务ID
+     * @return 视频审核任务
+     */
+    SecondVideoTask getTaskByTaskId(String taskId);
+
+    /**
+     * 处理所有待处理的视频审核任务
+     * @return 处理结果
+     */
+    String processAllPendingVideoTasks();
+}

+ 385 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/PlatformSecondTradeServiceImpl.java

@@ -0,0 +1,385 @@
+package shop.alien.second.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.swagger.models.auth.In;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.second.SecondRiskControlRecord;
+import shop.alien.entity.second.SecondTradeOperation;
+import shop.alien.entity.second.SecondTradeRecord;
+import shop.alien.entity.second.vo.SecondTradeRecordVo;
+import shop.alien.mapper.second.SecondRiskControlRecordMapper;
+import shop.alien.mapper.second.SecondTradeOperationMapper;
+import shop.alien.mapper.second.SecondTradeRecordMapper;
+import shop.alien.second.service.PlatformSecondTradeService;
+import shop.alien.second.service.SecondGoodsService;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class PlatformSecondTradeServiceImpl extends ServiceImpl<SecondTradeRecordMapper, SecondTradeRecord> implements PlatformSecondTradeService {
+
+    @Autowired
+    private SecondTradeRecordMapper secondTradeRecordMapper;
+    @Autowired
+    private SecondTradeOperationMapper secondTradeOperationMapper;
+    @Autowired
+    private SecondRiskControlRecordMapper secondRiskControlRecordMapper;
+    @Lazy
+    @Autowired
+    private SecondGoodsService secondGoodsService;
+
+    @Override
+    public IPage<SecondTradeRecordVo> getTradeRecordPage(SecondTradeRecordVo vo) throws Exception {
+        try {
+            Page<SecondTradeRecord> page = new Page<>(vo.getPageNum(), vo.getPageSize());
+            QueryWrapper<SecondTradeRecord> wrapper = new QueryWrapper<>();
+            wrapper.like(null != vo.getGoodsId(), "trade.goods_id", vo.getGoodsId());
+            wrapper.like(StringUtils.isNotBlank(vo.getTradeNo()), "trade.trade_no", vo.getTradeNo());
+            wrapper.eq(null != vo.getTradeStatus(), "trade.trade_status", vo.getTradeStatus());
+            wrapper.eq("trade.delete_flag", 0);
+            wrapper.orderByDesc("trade.created_time");
+            return secondTradeRecordMapper.getTradeRecordPage(page, wrapper);
+        } catch (Exception e) {
+            log.error("PlatformSecondTradeServiceImpl.getTradeRecordPage(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public List<SecondTradeRecordVo> getTradeRecordList(Integer userId, String beginTime, String endTime) throws Exception {
+        try {
+            Page<SecondTradeRecord> page = new Page<>(1, 1000000);
+            QueryWrapper<SecondTradeRecord> wrapper = new QueryWrapper<>();
+            wrapper.eq("trade.delete_flag", 0);
+            wrapper.eq("trade.trade_status", 4);
+            wrapper.ge("trade.transaction_time", StringUtils.isEmpty(beginTime) ? "1970-01-01 00:00:00" : beginTime);
+            wrapper.le("trade.transaction_time", StringUtils.isEmpty(endTime) ? "9999-12-31 23:59:59" : endTime);
+            wrapper.apply(" (trade.buyer_id = " + userId + " or trade.seller_id = " + userId + ") ");
+            wrapper.orderByDesc("trade.created_time");
+            List<SecondTradeRecordVo> voList = secondTradeRecordMapper.getTradeRecordPage(page, wrapper).getRecords();
+            for (SecondTradeRecordVo item : voList) {
+                item.setOperationJsonList(getOperationJsonList(item.getId()));
+            }
+            return voList;
+        } catch (Exception e) {
+            log.error("PlatformSecondTradeServiceImpl.getTradeRecordList(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public SecondTradeRecordVo getTradeRecordById(Integer id) throws Exception {
+        try {
+            // 交易信息
+            SecondTradeRecordVo vo = secondTradeRecordMapper.getTradeRecordById(id);
+            // 交易流程
+            vo.setOperationJsonList(getOperationJsonList(id));
+            // 商品信息
+            vo.setGoodsInfo(secondGoodsService.dealSecondGoodsRecordInfo(vo.getGoodsRecordId()));
+            return vo;
+        } catch (Exception e) {
+            log.error("PlatformSecondTradeServiceImpl.getTradeRecordById(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public List<JSONObject> getOperationJsonList(Integer tradeId) throws Exception {
+        try {
+            List<JSONObject> operationJsonList = new ArrayList<>();
+            SecondTradeRecord record = secondTradeRecordMapper.selectById(tradeId);
+            List<SecondTradeOperation> operationList = secondTradeOperationMapper.selectList(
+                    new QueryWrapper<SecondTradeOperation>().eq("trade_id", tradeId).eq("delete_flag", 0));
+
+            // 发起交易
+            JSONObject createJson = new JSONObject();
+            createJson.put("flag", 1);
+            createJson.put("message", "发起交易");
+            createJson.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(record.getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
+            operationJsonList.add(createJson);
+
+            // 发起交易后取消交易
+            if (operationList.size() == 2 && 7 == operationList.get(1).getType()) {
+                JSONObject cancelJson = new JSONObject();
+                cancelJson.put("flag", 1);
+                cancelJson.put("message", "取消交易");
+                cancelJson.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(operationList.get(1).getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
+                operationJsonList.add(cancelJson);
+                return operationJsonList;
+            }
+
+            // 拒绝
+            SecondTradeOperation refuse = operationList.stream().filter(item -> 2 == item.getType()).findFirst().orElse(null);
+            if (null != refuse) {
+                JSONObject refuseJson = new JSONObject();
+                refuseJson.put("flag", 1);
+                refuseJson.put("message", "拒绝交易");
+                refuseJson.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(refuse.getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
+                operationJsonList.add(refuseJson);
+                return operationJsonList;
+            }
+
+            // 确认
+            SecondTradeOperation confirm = operationList.stream().filter(item -> 3 == item.getType()).findFirst().orElse(null);
+            if (null != confirm) {
+                JSONObject confirmJson = new JSONObject();
+                confirmJson.put("flag", 1);
+                confirmJson.put("message", "确认交易");
+                confirmJson.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(confirm.getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
+                operationJsonList.add(confirmJson);
+            } else {
+                JSONObject confirmJson = new JSONObject();
+                confirmJson.put("flag", 0);
+                confirmJson.put("message", "等待确认交易");
+                confirmJson.put("time", "");
+                operationJsonList.add(confirmJson);
+            }
+
+            // 确认交易后取消交易
+            if (operationList.size() == 3 && 7 == operationList.get(2).getType()) {
+                JSONObject cancelJson = new JSONObject();
+                cancelJson.put("flag", 1);
+                cancelJson.put("message", "取消交易");
+                cancelJson.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(operationList.get(2).getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
+                operationJsonList.add(cancelJson);
+                return operationJsonList;
+            }
+
+            // 签到
+            List<SecondTradeOperation> signIn = operationList.stream().filter(item -> 4 == item.getType()).sorted(Comparator.comparing(SecondTradeOperation::getCreatedTime)).collect(Collectors.toList());
+            // 确认交易
+            List<SecondTradeOperation> successFail = operationList.stream().filter(item -> (5 == item.getType() || 6 == item.getType()) && 0 != item.getUserId()).sorted(Comparator.comparing(SecondTradeOperation::getCreatedTime)).collect(Collectors.toList());
+            // 系统确认的交易失败(超过交易时间12小时未确认成功失败,将自动视为失败)
+            SecondTradeOperation systemFail = operationList.stream().filter(item -> 6 == item.getType() && 0 == item.getUserId()).findFirst().orElse(null);
+
+            if (CollectionUtil.isEmpty(signIn)) {
+                JSONObject signInJson = new JSONObject();
+                signInJson.put("flag", CollectionUtil.isEmpty(successFail) && null == systemFail ? 0 : 2);
+                signInJson.put("message", CollectionUtil.isEmpty(successFail) && null == systemFail ? "等待买方签到" : "买方未签到");
+                signInJson.put("time", "");
+                operationJsonList.add(signInJson);
+
+                signInJson = new JSONObject();
+                signInJson.put("flag", CollectionUtil.isEmpty(successFail) && null == systemFail ? 0 : 2);
+                signInJson.put("message", CollectionUtil.isEmpty(successFail) && null == systemFail ? "等待卖方签到" : "卖方未签到");
+                signInJson.put("time", "");
+                operationJsonList.add(signInJson);
+            } else {
+                // 第一个人签到
+                JSONObject signInJson = new JSONObject();
+                signInJson.put("flag", 1);
+                signInJson.put("message", (Objects.equals(record.getBuyerId(), signIn.get(0).getUserId()) ? "买家" : "卖家") + "已签到");
+                signInJson.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(signIn.get(0).getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
+                operationJsonList.add(signInJson);
+
+                // 第二个人签到
+                if (signIn.size() > 1) {
+                    JSONObject signInJson2 = new JSONObject();
+                    signInJson2.put("flag", 1);
+                    signInJson2.put("message", (Objects.equals(record.getBuyerId(), signIn.get(1).getUserId()) ? "买家" : "卖家") + "已签到");
+                    signInJson2.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(signIn.get(1).getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
+                    operationJsonList.add(signInJson2);
+                } else {
+                    JSONObject signInJson2 = new JSONObject();
+                    signInJson2.put("flag", CollectionUtil.isEmpty(successFail) ? 0 : 2);
+                    signInJson2.put("message", (Objects.equals(record.getBuyerId(), signIn.get(0).getUserId()) ? "卖家" : "买家") + "未签到");
+                    signInJson2.put("time", "");
+                    operationJsonList.add(signInJson2);
+                }
+            }
+
+            // 签到后取消交易
+            if (CollectionUtil.isEmpty(successFail) && CollectionUtil.isNotEmpty(operationList) && 7 == operationList.get(operationList.size() - 1).getType()) {
+                JSONObject cancelJson = new JSONObject();
+                cancelJson.put("flag", 1);
+                cancelJson.put("message", "取消交易");
+                cancelJson.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(operationList.get(operationList.size() - 1).getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
+                operationJsonList.add(cancelJson);
+                return operationJsonList;
+            }
+
+            // 确认交易
+            if (CollectionUtil.isEmpty(successFail)) {
+                JSONObject signInJson = new JSONObject();
+                signInJson.put("flag", null == systemFail ? 0 : 2);
+                signInJson.put("message", null == systemFail ? "等待买方确认交易" : "买方未确认交易");
+                signInJson.put("time", "");
+                operationJsonList.add(signInJson);
+
+                signInJson = new JSONObject();
+                signInJson.put("flag", null == systemFail ? 0 : 2);
+                signInJson.put("message", null == systemFail ? "等待卖方确认交易" : "卖方未确认交易");
+                signInJson.put("time", "");
+                operationJsonList.add(signInJson);
+            } else {
+                // 第一个人确认交易
+                JSONObject successFailJson = new JSONObject();
+                successFailJson.put("flag", 1);
+                successFailJson.put("message", (Objects.equals(record.getBuyerId(), successFail.get(0).getUserId()) ? "买家" : "卖家") + "确认交易" +
+                        (5 == successFail.get(0).getType() ? "成功" : "失败"));
+                successFailJson.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(successFail.get(0).getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
+                operationJsonList.add(successFailJson);
+
+                // 第二个人确认交易
+                if (successFail.size() > 1) {
+                    JSONObject successFailJson2 = new JSONObject();
+                    successFailJson2.put("flag", 1);
+                    successFailJson2.put("message", (Objects.equals(record.getBuyerId(), successFail.get(1).getUserId()) ? "买家" : "卖家") + "确认交易" +
+                            (5 == successFail.get(1).getType() ? "成功" : "失败"));
+                    successFailJson2.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(successFail.get(1).getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
+                    operationJsonList.add(successFailJson2);
+                } else {
+                    JSONObject successFailJson2 = new JSONObject();
+                    successFailJson2.put("flag", Objects.equals(record.getSellerId(), successFail.get(0).getUserId()) ? 2 : 0);
+                    successFailJson2.put("message", (Objects.equals(record.getBuyerId(), successFail.get(0).getUserId()) ? "卖家" : "买家") + "未确认交易");
+                    successFailJson2.put("time", "");
+                    operationJsonList.add(successFailJson2);
+                }
+            }
+
+            // 交易完成
+            if (4 == record.getTradeStatus()) {
+                JSONObject finishJson = new JSONObject();
+                finishJson.put("flag", 1);
+                finishJson.put("message", "交易成功");
+                finishJson.put("time", "");
+                operationJsonList.add(finishJson);
+            } else if (5 == record.getTradeStatus()) {
+                JSONObject finishJson = new JSONObject();
+                finishJson.put("flag", 1);
+                finishJson.put("message", "交易失败");
+                finishJson.put("time", "");
+                operationJsonList.add(finishJson);
+            } else {
+                JSONObject finishJson = new JSONObject();
+                finishJson.put("flag", 0);
+                finishJson.put("message", "交易成功");
+                finishJson.put("time", "");
+                operationJsonList.add(finishJson);
+            }
+
+            return operationJsonList;
+        } catch (Exception e) {
+            log.error("PlatformSecondTradeServiceImpl.getOperationJsonList Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public List<JSONObject> getRankingList(String beginTime, String endTime) throws Exception {
+        try {
+            if (StringUtils.isBlank(beginTime)) beginTime = "1970-01-01 00:00:00";
+            if (StringUtils.isBlank(endTime)) endTime = "9999-12-31 23:59:59";
+            return secondTradeRecordMapper.getRankingList(beginTime, endTime);
+        } catch (Exception e) {
+            log.error("PlatformSecondTradeServiceImpl.getRankingList Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public void setTradeFailure(Integer tradeId, String failureReason) throws Exception {
+        try {
+            LambdaUpdateWrapper<SecondTradeRecord> wrapper = new LambdaUpdateWrapper<>();
+            wrapper.eq(SecondTradeRecord::getId, tradeId);
+            wrapper.set(SecondTradeRecord::getFailureFlag, 1);
+            wrapper.set(SecondTradeRecord::getFailureReason, failureReason);
+            secondTradeRecordMapper.update(null, wrapper);
+        } catch (Exception e) {
+            log.error("PlatformSecondTradeServiceImpl.setTradeFailure Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public List<SecondTradeRecordVo> getTradeRecordListByRiskId(Integer id) throws Exception {
+        try {
+            SecondRiskControlRecord record = secondRiskControlRecordMapper.selectById(id);
+            Page<SecondTradeRecord> page = new Page<>(1, 1000000);
+            QueryWrapper<SecondTradeRecord> wrapper = new QueryWrapper<>();
+            wrapper.in("trade.id", JSONArray.parseArray(record.getDetailInfo()));
+            List<SecondTradeRecordVo> voList = secondTradeRecordMapper.getTradeRecordPage(page, wrapper).getRecords();
+            for (SecondTradeRecordVo item : voList) {
+                item.setOperationJsonList(getOperationJsonList(item.getId()));
+            }
+            return voList;
+        } catch (Exception e) {
+            log.error("PlatformSecondTradeServiceImpl.getTradeRecordListByRiskId(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void bannedAccountCancelTrade(Integer userId) throws Exception {
+        try {
+            // 查询封禁信息
+            LambdaQueryWrapper<SecondRiskControlRecord> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(SecondRiskControlRecord::getUserId, userId);
+            wrapper.eq(SecondRiskControlRecord::getRuleType, 1);
+            wrapper.eq(SecondRiskControlRecord::getRiskStatus, 0);
+            List<SecondRiskControlRecord> list = secondRiskControlRecordMapper.selectList(wrapper);
+            List<String> infoList = list.stream().map(SecondRiskControlRecord::getDetailInfo).collect(Collectors.toList());
+            List<JSONArray> array = infoList.stream().map(JSONArray::parseArray).collect(Collectors.toList());
+            List<Integer> idList = new ArrayList<>();
+            for (JSONArray item : array) {
+                idList.addAll(item.toJavaList(Integer.class));
+            }
+            idList = idList.stream().distinct().collect(Collectors.toList());
+
+            // 查询需要取消的交易信息
+            LambdaQueryWrapper<SecondTradeRecord> wrapper1 = new LambdaQueryWrapper<>();
+            List<Integer> finalIdList = idList;
+            wrapper1.eq(SecondTradeRecord::getTradeStatus, 3)
+                    .and(item -> item.in(SecondTradeRecord::getId, finalIdList)
+                            .or(io -> io.eq(SecondTradeRecord::getSellerId, userId)
+                                    .between(SecondTradeRecord::getTransactionTime, LocalDate.now() + " 00:00:00", LocalDate.now().plusDays(7) + " 23:59:59")));
+//                    .or(item -> item.eq(SecondTradeRecord::getSellerId, userId)
+//                            .between(SecondTradeRecord::getTransactionTime, LocalDate.now() + " 00:00:00", LocalDate.now().plusDays(7) + " 23:59:59"));
+            List<SecondTradeRecord> tradeList = secondTradeRecordMapper.selectList(wrapper1);
+            List<Integer> tradeIds = tradeList.stream().map(SecondTradeRecord::getId).distinct().collect(Collectors.toList());
+
+            if (CollectionUtil.isEmpty(tradeIds)) return;
+
+            // 取消交易
+            LambdaUpdateWrapper<SecondTradeRecord> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.in(SecondTradeRecord::getId, tradeIds);
+            updateWrapper.set(SecondTradeRecord::getTradeStatus, 6);
+            secondTradeRecordMapper.update(null, updateWrapper);
+
+            // 添加交易操作记录
+            for (Integer id : tradeIds) {
+                SecondTradeOperation operation = new SecondTradeOperation();
+                operation.setTradeId(id);
+                operation.setUserId(0);
+                operation.setType(7);
+                secondTradeOperationMapper.insert(operation);
+            }
+        } catch (Exception e) {
+            log.error("PlatformSecondTradeServiceImpl.bannedAccountCancelTrade(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+}

+ 122 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/PlatformUserViolationServiceImpl.java

@@ -0,0 +1,122 @@
+package shop.alien.second.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.entity.second.vo.SecondUserViolationDetailVo;
+import shop.alien.entity.second.vo.SecondUserViolationVo;
+import shop.alien.entity.store.LifeUserViolation;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.mapper.LifeUserViolationMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.second.SecondGoodsMapper;
+import shop.alien.mapper.second.SecondUserViolationMapper;
+import shop.alien.second.service.PlatformUserViolationService;
+import shop.alien.second.service.SecondGoodsService;
+import shop.alien.util.common.Constants;
+
+import java.util.*;
+
+@Slf4j
+@Service
+public class PlatformUserViolationServiceImpl extends ServiceImpl<SecondUserViolationMapper, SecondUserViolationVo> implements PlatformUserViolationService {
+
+    List<String> videoFileType = Arrays.asList("mp4", "avi", "flv", "mkv", "rmvb", "wmv", "3gp", "mov");
+
+    @Autowired
+    private SecondUserViolationMapper mapper;
+
+    @Autowired
+    private LifeUserViolationMapper lifeUserViolationMapper;
+
+    @Autowired
+    private SecondGoodsMapper secondGoodsMapper;
+
+    @Autowired
+    private StoreImgMapper storeImgMapper;
+
+    @Lazy
+    @Autowired
+    private SecondGoodsService secondGoodsService;
+
+
+    @Override
+    public IPage<SecondUserViolationVo> getUserViolationByPage(IPage<SecondUserViolationVo> page, String reportingUserName,
+                                                               String reportingDate, String processingStatus, String reportContextType) throws Exception {
+        try {
+            return mapper.getUserViolationByPage(page, reportingUserName, reportingDate, processingStatus, reportContextType);
+        } catch (Exception e) {
+            log.error("PlatformUserViolationServiceImpl.getUserViolationByPage Error Mgs={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public SecondUserViolationDetailVo getUserViolationDetail(Integer id) throws Exception {
+        try {
+            // 查询举报信息
+            SecondUserViolationDetailVo item =mapper.getUserViolationInfo(id);
+
+            // 商品的时候查询商品信息
+            if ("4".equals(item.getReportContextType())) {
+                SecondGoodsVo secondGoods = secondGoodsService.dealSecondGoodsRecordInfo(item.getBusinessId());
+                item.setSecondGoods(secondGoods);
+            }
+
+            // 图片list
+            List<Map<String, Object>> list = new ArrayList<>();
+
+            // 查询图片信息
+            LambdaQueryWrapper<StoreImg> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(StoreImg::getStoreId, item.getId());
+            wrapper.eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_REPORT);
+            List<StoreImg> imgList = storeImgMapper.selectList(wrapper);
+
+            for (StoreImg img : imgList) {
+                Map<String, Object> map = new HashMap<>();
+                String fileType = img.getImgUrl().substring(img.getImgUrl().lastIndexOf(".") + 1);
+                if (videoFileType.contains(fileType.toLowerCase())) {
+                    map.put("type", "video");
+                } else {
+                    map.put("type", "image");
+                }
+                map.put("imgUrl", img.getImgUrl());
+                list.add(map);
+            }
+            item.setImgList(list);
+            return item;
+        } catch (Exception e) {
+            log.error("PlatformUserViolationServiceImpl.getUserViolationByPage Error Mgs={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public int updateUserViolation(LifeUserViolation row) throws Exception {
+        LambdaUpdateWrapper<LifeUserViolation> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper
+                .eq(LifeUserViolation::getId, row.getId())
+                .set(LifeUserViolation::getProcessingStatus, row.getProcessingStatus())
+                .set(LifeUserViolation::getProcessingTime, new Date())
+                .set(LifeUserViolation::getReportResult, row.getReportResult());
+        return lifeUserViolationMapper.update(null, updateWrapper);
+    }
+
+    private static boolean isVideoUrl(String url) {
+        if (url == null) return false;
+        url = url.toLowerCase();
+        return url.endsWith(".mp4") ||
+                url.endsWith(".avi") ||
+                url.endsWith(".mov") ||
+                url.endsWith(".flv") ||
+                url.endsWith(".wmv") ||
+                url.endsWith(".mkv");
+    }
+
+}

+ 317 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/RiskControlGoodsServiceImpl.java

@@ -0,0 +1,317 @@
+package shop.alien.second.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.SecondGoodsRecord;
+import shop.alien.entity.second.vo.SecondGoodsRecordDetailVo;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.mapper.LifeUserMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.second.SecondGoodsMapper;
+import shop.alien.mapper.second.SecondGoodsRecordMapper;
+import shop.alien.second.service.RiskControlGoodsService;
+import shop.alien.util.common.Constants;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 风控商品服务实现类
+ * 提供风控相关的商品信息查询服务,包括通过记录ID和商品ID批量查询商品详细信息
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class RiskControlGoodsServiceImpl implements RiskControlGoodsService {
+
+    /**
+     * 注入商品记录Mapper
+      */
+    private final SecondGoodsRecordMapper secondGoodsRecordMapper;
+
+    /**
+     * 注入商品Mapper
+     */
+    private final SecondGoodsMapper secondGoodsMapper;
+
+    /**
+     * 注入图片Mapper
+      */
+    private final StoreImgMapper storeImgMapper;
+
+    /**
+     * 注入用户Mapper
+     */
+    private final LifeUserMapper lifeUserMapper;
+
+    /**
+     * 通过商品记录表ID批量查询商品信息(包含图片)
+     * @param recordIds 商品记录表ID列表
+     * @return 商品信息列表
+     */
+    @Override
+    public List<SecondGoodsRecordDetailVo> getGoodsInfoByRecordIds(List<Integer> recordIds) {
+        // 检查传入ID列表是否为空,如果为空则直接返回空列表
+        if (CollectionUtils.isEmpty(recordIds)) {
+            return new ArrayList<>();
+        }
+
+        // 构建查询条件,根据记录ID列表查询商品记录信息
+        QueryWrapper<SecondGoodsRecord> queryWrapper = new QueryWrapper<>();
+        queryWrapper.in("sg.id", recordIds);
+        // 执行查询获取商品记录列表
+        List<SecondGoodsRecord> records = secondGoodsRecordMapper.selectdminGoodsList(queryWrapper);
+        
+        // 检查查询结果是否为空,如果为空则直接返回空列表
+        if (CollectionUtils.isEmpty(records)) {
+            return new ArrayList<>();
+        }
+
+        // 批量获取用户和图片信息
+        BatchQueryResult<SecondGoodsRecord> batchResult = batchQueryUserInfoAndImageInfo(records, 
+            SecondGoodsRecord::getUserId, SecondGoodsRecord::getId);
+
+        // 创建结果列表,用于存储最终的商品记录详情VO对象
+        List<SecondGoodsRecordDetailVo> result = new ArrayList<>();
+        // 遍历所有商品记录,构建对应的VO对象
+        for (SecondGoodsRecord record : records) {
+            // 根据商品记录创建VO对象
+            SecondGoodsRecordDetailVo detailVo = SecondGoodsRecordDetailVo.fromRecord(record);
+
+            // 如果记录中有用户ID且在用户映射表中存在该用户,则设置用户信息
+            if (record.getUserId() != null && batchResult.userMap.containsKey(record.getUserId())) {
+                // 从用户映射表中获取用户信息
+                LifeUser user = batchResult.userMap.get(record.getUserId());
+                // 设置用户名和用户手机号
+                detailVo.setUserName(user.getUserName());
+                detailVo.setUserPhone(user.getUserPhone());
+            }
+
+            // 如果图片映射表中包含该记录的ID,则设置图片信息
+            if (batchResult.imageMap.containsKey(record.getId())) {
+                // 从图片映射表中获取该记录的图片列表
+                List<StoreImg> recordImages = batchResult.imageMap.get(record.getId());
+                // 提取图片URL列表
+                List<String> imageUrls = recordImages.stream()
+                        .map(StoreImg::getImgUrl)
+                        .collect(Collectors.toList());
+                // 处理图片列表(区分图片和视频)
+                List<Map<String, Object>> imgList = processReportImages(recordImages, 2);
+                // 设置图片列表和图片URL列表
+                detailVo.setImgList(imgList);
+                detailVo.setImageUrls(imageUrls);
+            }
+
+            // 将构建好的VO对象添加到结果列表中
+            result.add(detailVo);
+        }
+
+        // 返回最终的结果列表
+        return result;
+    }
+
+    /**
+     * 通过商品表ID批量查询商品信息(包含图片)
+     * @param goodsIds 商品表ID列表
+     * @return 商品信息列表
+     */
+    @Override
+    public List<SecondGoodsVo> getGoodsInfoByGoodsIds(List<Integer> goodsIds) {
+        // 检查传入ID列表是否为空,如果为空则直接返回空列表
+        if (CollectionUtils.isEmpty(goodsIds)) {
+            return new ArrayList<>();
+        }
+
+        // 构建查询条件,根据商品ID列表查询商品信息
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.in("sg.id", goodsIds);
+        // 执行查询获取商品列表
+        List<SecondGoodsVo> secondGoodsList = secondGoodsMapper.selectGoodsList(queryWrapper);
+
+        // 检查查询结果是否为空,如果为空则直接返回空列表
+        if (CollectionUtils.isEmpty(secondGoodsList)) {
+            return new ArrayList<>();
+        }
+
+        // 批量获取用户和图片信息
+        BatchQueryResult<SecondGoodsVo> batchResult = batchQueryUserInfoAndImageInfo(secondGoodsList,
+            SecondGoods::getUserId, SecondGoods::getId);
+
+        // 创建结果列表,用于存储最终的商品VO对象
+        List<SecondGoodsVo> result = new ArrayList<>();
+        // 遍历所有商品,构建对应的VO对象
+        for (SecondGoods secondGoods : secondGoodsList) {
+            // 创建新的商品VO对象
+            SecondGoodsVo secondGoodsVo = new SecondGoodsVo();
+            // 将商品属性复制到VO对象中
+            BeanUtils.copyProperties(secondGoods, secondGoodsVo);
+
+            // 如果VO对象中有用户ID且在用户映射表中存在该用户,则设置用户信息
+            if (secondGoodsVo.getUserId() != null && batchResult.userMap.containsKey(secondGoodsVo.getUserId())) {
+                // 从用户映射表中获取用户信息
+                LifeUser user = batchResult.userMap.get(secondGoodsVo.getUserId());
+                // 设置用户名和用户手机号
+                secondGoodsVo.setUserName(user.getUserName());
+                secondGoodsVo.setUserPhone(user.getUserPhone());
+            }
+
+            // 如果图片映射表中包含该商品的ID,则设置图片信息
+            if (batchResult.imageMap.containsKey(secondGoodsVo.getId())) {
+                // 从图片映射表中获取该商品的图片列表
+                List<StoreImg> recordImages = batchResult.imageMap.get(secondGoodsVo.getId());
+                // 提取图片URL列表
+                List<String> imageUrls = recordImages.stream()
+                        .map(StoreImg::getImgUrl)
+                        .collect(Collectors.toList());
+                // 处理图片列表(区分图片和视频)
+                List<Map<String, Object>> imgList = processReportImages(recordImages, 2);
+                // 设置图片列表和图片URL列表
+                secondGoodsVo.setImgList(imgList);
+                secondGoodsVo.setImageUrls(imageUrls);
+            }
+
+            // 将构建好的VO对象添加到结果列表中
+            result.add(secondGoodsVo);
+        }
+
+        // 返回最终的结果列表
+        return result;
+    }
+
+    /**
+     * 批量查询用户信息和图片信息的通用方法
+     * @param records 商品记录列表
+     * @param userIdGetter 获取用户ID的函数
+     * @param entityIdGetter 获取实体ID的函数(可能是记录ID或商品ID)
+     * @param <T> 记录类型
+     * @return 批量查询结果
+     */
+    private <T> BatchQueryResult<T> batchQueryUserInfoAndImageInfo(List<T> records, 
+            Function<T, Integer> userIdGetter, Function<T, Integer> entityIdGetter) {
+        
+        // 从商品记录中提取所有非空的用户ID并去重,用于后续批量查询用户信息
+        List<Integer> userIds = records.stream()
+                .map(userIdGetter)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+        // 创建用户映射表,用于存储用户ID到用户信息的映射关系
+        Map<Integer, LifeUser> userMap = new HashMap<>();
+        // 如果存在用户ID,则批量查询用户信息
+        if (!CollectionUtils.isEmpty(userIds)) {
+            // 构建用户查询条件,根据用户ID列表和未删除标识查询用户信息
+            QueryWrapper<LifeUser> userQueryWrapper = new QueryWrapper<>();
+            userQueryWrapper.lambda()
+                    .in(LifeUser::getId, userIds)
+                    .eq(LifeUser::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED);
+            // 执行查询获取用户列表
+            List<LifeUser> users = lifeUserMapper.selectList(userQueryWrapper);
+            // 将用户列表转换为以用户ID为键的映射表,使用合并函数处理可能的重复键
+            userMap = users.stream().collect(Collectors.toMap(
+                LifeUser::getId,
+                user -> user,
+                (existing, replacement) -> existing // 处理键冲突,保留第一个值
+            ));
+        }
+
+        // 从商品记录中提取所有实体ID,用于后续查询图片信息
+        List<Integer> entityIdList = records.stream()
+                .map(entityIdGetter)
+                .collect(Collectors.toList());
+
+        // 构建图片查询条件,根据实体ID列表、图片类型和未删除标识查询图片信息
+        QueryWrapper<StoreImg> imageQueryWrapper = new QueryWrapper<>();
+        imageQueryWrapper.lambda()
+                .in(StoreImg::getStoreId, entityIdList)
+                .eq(StoreImg::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED)
+                .orderByAsc(StoreImg::getImgSort);
+        
+        // 根据记录类型设置不同的图片类型
+        if (!records.isEmpty()) {
+            T firstRecord = records.get(0);
+            if (firstRecord instanceof SecondGoodsRecord) {
+                // 如果是商品记录类型,使用SECOND_HAND_RECORD类型
+                imageQueryWrapper.lambda()
+                        .eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_RECORD);
+            } else if (firstRecord instanceof SecondGoodsVo) {
+                // 如果是商品类型,使用SECOND_HAND类型
+                imageQueryWrapper.lambda()
+                        .eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_GOODS);
+            }
+        }
+        
+        // 执行查询获取图片列表
+        List<StoreImg> imageList = storeImgMapper.selectList(imageQueryWrapper);
+
+        // 将图片列表按照实体ID进行分组,方便后续关联到对应记录
+        Map<Integer, List<StoreImg>> imageMap = imageList.stream()
+                .collect(Collectors.groupingBy(StoreImg::getStoreId));
+        
+        // 返回批量查询结果
+        return new BatchQueryResult<>(userMap, imageMap);
+    }
+
+    /**
+     * 批量查询结果类
+     * @param <T> 记录类型
+     */
+    private static class BatchQueryResult<T> {
+        final Map<Integer, LifeUser> userMap;
+        final Map<Integer, List<StoreImg>> imageMap;
+
+        public BatchQueryResult(Map<Integer, LifeUser> userMap, Map<Integer, List<StoreImg>> imageMap) {
+            this.userMap = userMap;
+            this.imageMap = imageMap;
+        }
+    }
+
+    /**
+     * 处理图片列表,区分图片和视频类型
+     * @param imageList 图片URL集合
+     * @param type  类型 1-举报 2-商品
+     * @return 图片列表,每个元素包含类型和URL信息
+     */
+    private List<Map<String, Object>> processReportImages(List<StoreImg> imageList, Integer type) {
+        // 创建结果列表
+        List<Map<String, Object>> list = new ArrayList<>();
+        // 定义视频文件类型扩展名列表
+        List<String> videoFileType = Arrays.asList("mp4", "avi", "flv", "mkv", "rmvb", "wmv", "3gp", "mov");
+
+        // 遍历所有图片对象
+        for (StoreImg img : imageList) {
+            // 创建映射对象存储图片信息
+            Map<String, Object> map = new HashMap<>();
+            // 从图片URL中提取文件扩展名
+            String fileType = img.getImgUrl().substring(img.getImgUrl().lastIndexOf(".") + 1);
+            // 判断文件类型是否为视频
+            if (videoFileType.contains(fileType.toLowerCase())) {
+                // 如果是视频,设置类型为video
+                map.put("type", "video");
+            } else {
+                // 如果不是视频,设置类型为image
+                map.put("type", "image");
+            }
+            // 设置图片URL
+            map.put("imgUrl", img.getImgUrl());
+            // 如果类型为1(举报类型),则设置视频URL
+            if (type == 1) {
+                map.put("videoUrl", img.getImgUrl());
+            }
+            // 将映射对象添加到结果列表中
+            list.add(map);
+        }
+
+        // 返回处理后的图片列表
+        return list;
+    }
+
+}

+ 267 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/RiskControlServiceImpl.java

@@ -0,0 +1,267 @@
+package shop.alien.second.service.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.second.SecondRiskControlRecord;
+import shop.alien.entity.second.SecondUserCredit;
+import shop.alien.entity.second.SecondUserCreditRecord;
+import shop.alien.entity.second.vo.*;
+import shop.alien.entity.store.LifeUser;
+
+import shop.alien.entity.store.LifeNotice;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.mapper.LifeUserMapper;
+import shop.alien.mapper.second.SecondRiskControlRecordMapper;
+import shop.alien.mapper.second.SecondUserCreditMapper;
+import shop.alien.second.feign.AlienStoreFeign;
+import shop.alien.second.service.RiskControlService;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 风控服务实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class RiskControlServiceImpl extends ServiceImpl<SecondRiskControlRecordMapper, SecondRiskControlRecord> implements RiskControlService {
+
+    private final SecondRiskControlRecordMapper secondRiskControlRecordMapper;
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final LifeUserMapper lifeUserMapper;
+    private final AlienStoreFeign alienStoreFeign;
+
+    private final RiskControlGoodsServiceImpl riskControlGoodsService;
+    private final PlatformSecondTradeServiceImpl tradeService;
+    private final SecondUserCreditMapper secondUserCreditMapper;
+    private final SecondUserCreditRecordServiceImpl sreditRecordService;
+
+    @Lazy
+    @Autowired
+    private SecondGoodsServiceImpl secondGoodsService;
+    /**
+     * 记录风控数据
+     *
+     * @param userId     用户ID
+     * @param ruleType   规则类型 1:洗钱嫌疑 2:账号异常 3:交易欺诈 4:异常发布
+     * @param ruleName   规则名称
+     * @param businessId 业务ID
+     * @param detailInfo 详细信息(JSON格式)
+     */
+    @Override
+    public void recordRiskControlData(Integer userId,
+                                       Integer ruleType,
+                                       String ruleName,
+                                      String businessId,
+                                       String detailInfo) {
+        try {
+            SecondRiskControlRecord record = new SecondRiskControlRecord();
+            record.setUserId(userId);
+            record.setRuleType(ruleType);
+            record.setRuleName(ruleName);
+            record.setBusinessId(businessId);
+            record.setDetailInfo(detailInfo);
+            record.setCreatedTime(Date.from(LocalDateTime.now().plusDays(7).atZone(ZoneId.systemDefault()).toInstant()));
+            record.setUpdatedTime(Date.from(LocalDateTime.now().plusDays(7).atZone(ZoneId.systemDefault()).toInstant()));
+            record.setDeleteFlag(0);
+            record.setCreatedUserId(userId);
+            record.setUpdatedUserId(userId);
+
+            secondRiskControlRecordMapper.insert(record);
+        } catch (Exception e) {
+            log.error("记录风控数据时发生异常: 用户ID={}, 规则类型={},业务id={}", userId, ruleType, businessId, e);
+        }
+    }
+
+    /**
+     * 获取相同类型的风控数据
+     *
+     * @param ruleType   规则类型 1:洗钱嫌疑 2:账号异常 3:交易欺诈 4:异常发布
+     * @param businessId 业务ID
+     * @return 相同类型的风控数据列表
+     */
+    @Override
+    public List<SecondRiskControlRecord> getSameTypeRiskControlRecords(Integer ruleType, String businessId) {
+        log.info("获取相同类型的风控数据: 规则类型={},业务id={}", ruleType, businessId);
+        QueryWrapper<SecondRiskControlRecord> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("rule_type", ruleType);
+        queryWrapper.eq("business_id", businessId);
+        log.info("查询条件: {}", queryWrapper.getSqlSegment());
+        return secondRiskControlRecordMapper.selectList(queryWrapper);
+    }
+
+    @Override
+    public IPage<SecondRiskControlRecordVo> queryRiskControlRecords(SecondRiskControlRecordVo vo) {
+        try {
+            // 3. 获取商品操作记录集合
+            QueryWrapper<SecondRiskControlRecordVo> recordQueryWrapper = new QueryWrapper<>();
+            recordQueryWrapper
+                    .eq(vo.getRuleType() != null, "r.rule_type", vo.getRuleType())
+                    .eq(vo.getRiskStatus() != null, "r.risk_status", vo.getRiskStatus())
+                    .eq("r.delete_flag",0)
+                    .orderByDesc("r.created_time");
+            return secondRiskControlRecordMapper.queryRiskControlRecords(new Page<>(vo.getPageNum(), vo.getPageSize()), recordQueryWrapper);
+        } catch (Exception e) {
+            log.error("记录风控数据时发生异常: ", e);
+            return null;
+        }
+    }
+
+
+    /**
+     * 发送封禁通知
+     */
+    public void sendNotice(Integer userId) {
+        LifeUser lifeUser = lifeUserMapper.selectById(userId);
+        if (lifeUser == null) return;
+
+        String phoneId = "user_" + lifeUser.getUserPhone();
+
+        // 给买家发送通知
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setSenderId("system");
+        lifeNotice.setReceiverId(phoneId);
+        lifeNotice.setBusinessId(userId);
+        lifeNotice.setTitle("账号违规处理");
+        lifeNotice.setNoticeType(1);
+        // 封装通知信息
+        JSONObject noticeMessage = new JSONObject();
+        noticeMessage.put("message", "账号违反平台规约进行封禁处理");
+        lifeNotice.setContext(noticeMessage.toJSONString());
+        lifeNoticeMapper.insert(lifeNotice);
+
+        // 给买家推送通知
+        WebSocketVo webSocketVo = new WebSocketVo();
+        webSocketVo.setSenderId("system");
+        webSocketVo.setReceiverId(phoneId);
+        webSocketVo.setCategory("notice");
+        webSocketVo.setNoticeType("1");
+        webSocketVo.setType("1");
+        webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+        alienStoreFeign.sendMsgToClientByPhoneId(phoneId, JSONObject.from(webSocketVo).toJSONString());
+    }
+
+
+    @Override
+    public SecondRiskRecordVo queryRiskControlRecordsDetail(SecondRiskRecordVo vo) throws Exception {
+        // 返回实体
+        SecondRiskRecordVo item = new SecondRiskRecordVo();
+
+        if (vo.getRuleType() == 1) {
+            item.setTradeRecordList(tradeService.getTradeRecordListByRiskId(vo.getId()));
+        } else if (vo.getRuleType() == 2) {
+            // 将数组转换为包含整数的列表
+            List<Integer> userIds = Arrays.stream(vo.getDetailInfo().split(","))
+                    .map(Integer::parseInt)
+                    .collect(Collectors.toList());
+
+            // 使用LambdaQueryWrapper进行查询
+            LambdaQueryWrapper<LifeUser> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.in(LifeUser::getId, userIds);
+            item.setUserList(lifeUserMapper.selectList(queryWrapper));
+        } else if (vo.getRuleType() == 3 || vo.getRuleType() == 4) {
+            // 移除方括号并按逗号分割,然后转换为Integer列表
+            List<Integer> integerList = Arrays.stream(vo.getDetailInfo().substring(1, vo.getDetailInfo().length() - 1)
+                            .split(","))
+                    .map(String::trim)
+                    .map(Integer::parseInt)
+                    .collect(Collectors.toList());
+
+            if (vo.getRuleType() == 3) {
+                item.setGoodsRecordList(riskControlGoodsService.getGoodsInfoByRecordIds(integerList));
+            } else {
+                item.setGoodsList(riskControlGoodsService.getGoodsInfoByGoodsIds(integerList));
+            }
+        }
+        return item;
+    }
+
+
+    @Override
+    public boolean accountBan(SecondRiskRecordVo vo) throws Exception {
+        if (1 == vo.getIsExecute()) {
+            // 封禁时间
+            Date freezeTime = Date.from(LocalDateTime.now().plusDays(7).atZone(ZoneId.systemDefault()).toInstant());
+
+            if (vo.getRuleType() == 2) {
+                // 查询所有待交易
+                LambdaQueryWrapper<SecondRiskControlRecord> wrapper = new LambdaQueryWrapper<>();
+                wrapper.eq(SecondRiskControlRecord::getBusinessId, vo.getBusinessId());
+                List<SecondRiskControlRecord> records = secondRiskControlRecordMapper.selectList(wrapper);
+
+                // 将所有记录的detailInfo用逗号连接在一起
+                String combinedDetailInfo = records.stream()
+                        .map(SecondRiskControlRecord::getDetailInfo)
+                        .filter(Objects::nonNull) // 过滤掉null值
+                        .collect(Collectors.joining(","));
+
+                // 将数组转换为包含整数的列表
+                Set<Integer> userIds = Arrays.stream(combinedDetailInfo.split(","))
+                        .map(Integer::parseInt)
+                        .collect(Collectors.toSet());
+
+                // 方式1: 使用LambdaUpdateWrapper
+                LambdaUpdateWrapper<SecondUserCredit> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.in(SecondUserCredit::getUserId, userIds)
+                        .set(SecondUserCredit::getFreezeTime, freezeTime)
+                        .set(SecondUserCredit::getUpdatedTime, new Date());
+                secondUserCreditMapper.update(null, updateWrapper);
+            } else {
+                LambdaQueryWrapper<SecondUserCredit> queryWrapper = new LambdaQueryWrapper<>();
+                queryWrapper.eq(SecondUserCredit::getUserId, vo.getUserId())
+                        .eq(SecondUserCredit::getDeleteFlag, 0);
+                SecondUserCredit userPoints = secondUserCreditMapper.selectOne(queryWrapper);
+                if (userPoints == null) {
+                    return false;
+                }
+                if (vo.getRuleType() == 1) {
+                    tradeService.bannedAccountCancelTrade(vo.getUserId());
+                } else {
+                    if (vo.getRuleType() == 3 || vo.getRuleType() == 4) {
+                        // 更新积分
+                        int newPoints = userPoints.getUserPoints() - 50;
+                        // 创建积分记录
+                        SecondUserCreditRecord record = new SecondUserCreditRecord();
+                        record.setUserId(vo.getUserId());
+                        record.setPoints(-50);
+                        record.setPointsType(3);
+                        sreditRecordService.createRecord(record);
+                        userPoints.setUserPoints(newPoints);
+                        secondGoodsService.batchShelveGoodsByRiskControlRecord(vo.getRuleType(), vo.getBusinessId());
+                    }
+                }
+                userPoints.setFreezeTime(freezeTime);
+                secondUserCreditMapper.updateById(userPoints);
+            }
+        }
+        // 方式1: 使用LambdaUpdateWrapper
+        LambdaUpdateWrapper<SecondRiskControlRecord> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(SecondRiskControlRecord::getDeleteFlag, 0)
+                .eq(SecondRiskControlRecord::getRuleType, vo.getRuleType())
+                .set(SecondRiskControlRecord::getRiskStatus, vo.getIsExecute())
+                .set(SecondRiskControlRecord::getUpdatedTime, new Date());
+        if (vo.getRuleType() == 4) {
+            updateWrapper.in(SecondRiskControlRecord::getUserId, vo.getUserId());
+
+        } else {
+            updateWrapper.in(SecondRiskControlRecord::getBusinessId, vo.getBusinessId());
+        }
+        secondRiskControlRecordMapper.update(null, updateWrapper);
+        return true;
+    }
+
+}

+ 296 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondEntrustUserServiceImpl.java

@@ -0,0 +1,296 @@
+package shop.alien.second.service.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.second.SecondEntrustUser;
+import shop.alien.entity.second.SecondTradeRecord;
+import shop.alien.entity.second.vo.SecondEntrustUserDTO;
+import shop.alien.entity.second.vo.SecondEntrustUserDetailVo;
+import shop.alien.entity.second.vo.SecondEntrustUserQueryVo;
+import shop.alien.entity.second.vo.SecondEntrustUserResultVo;
+import shop.alien.entity.second.vo.SecondTradeRecordVo;
+import shop.alien.mapper.second.SecondEntrustUserMapper;
+import shop.alien.mapper.second.SecondTradeRecordMapper;
+import shop.alien.second.service.PlatformSecondTradeService;
+import shop.alien.second.service.SecondEntrustUserService;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * 二手委托人信息表 服务实现类
+ * </p>
+ *
+ * @author ssk
+ * @since 2025-11-21
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondEntrustUserServiceImpl extends ServiceImpl<SecondEntrustUserMapper, SecondEntrustUser> implements SecondEntrustUserService {
+
+    private final SecondEntrustUserMapper secondEntrustUserMapper;
+    private final SecondTradeRecordMapper secondTradeRecordMapper;
+    private final PlatformSecondTradeService platformSecondTradeService;
+
+    /**
+     * 创建委托人信息
+     *
+     * @param dto 委托人信息DTO
+     * @return 是否创建成功
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean createEntrustUser(SecondEntrustUserDTO dto) {
+        log.info("SecondEntrustUserServiceImpl.createEntrustUser dto={}", dto);
+        try {
+            // 检查该交易是否已存在委托人信息
+            SecondEntrustUser existUser = getByTradeId(dto.getEntrustTradeId());
+            if (existUser != null) {
+                log.warn("交易ID:{}已存在委托人信息", dto.getEntrustTradeId());
+                throw new RuntimeException("该交易已存在委托人信息");
+            }
+
+            // 创建委托人信息
+            SecondEntrustUser entrustUser = new SecondEntrustUser();
+            BeanUtils.copyProperties(dto, entrustUser);
+            
+            return this.save(entrustUser);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserServiceImpl.createEntrustUser error: {}", e.getMessage(), e);
+            throw new RuntimeException("创建委托人信息失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据交易ID获取委托人信息
+     *
+     * @param entrustTradeId 交易ID
+     * @return 委托人信息
+     */
+    @Override
+    public SecondEntrustUser getByTradeId(Integer entrustTradeId) {
+        log.info("SecondEntrustUserServiceImpl.getByTradeId entrustTradeId={}", entrustTradeId);
+        LambdaQueryWrapper<SecondEntrustUser> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SecondEntrustUser::getEntrustTradeId, entrustTradeId);
+        return this.getOne(wrapper);
+    }
+
+    /**
+     * 根据交易编号获取委托人信息
+     *
+     * @param entrustTradeNo 交易编号
+     * @return 委托人信息
+     */
+    @Override
+    public SecondEntrustUser getByTradeNo(String entrustTradeNo) {
+        log.info("SecondEntrustUserServiceImpl.getByTradeNo entrustTradeNo={}", entrustTradeNo);
+        if (StringUtils.isBlank(entrustTradeNo)) {
+            return null;
+        }
+        LambdaQueryWrapper<SecondEntrustUser> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SecondEntrustUser::getEntrustTradeNo, entrustTradeNo);
+        return this.getOne(wrapper);
+    }
+
+    /**
+     * 更新委托人信息
+     *
+     * @param id 委托人ID
+     * @param dto 委托人信息DTO
+     * @return 是否更新成功
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean updateEntrustUser(Integer id, SecondEntrustUserDTO dto) {
+        log.info("SecondEntrustUserServiceImpl.updateEntrustUser id={}, dto={}", id, dto);
+        try {
+            // 检查委托人信息是否存在
+            SecondEntrustUser entrustUser = this.getById(id);
+            if (entrustUser == null) {
+                log.warn("委托人信息不存在, id={}", id);
+                throw new RuntimeException("委托人信息不存在");
+            }
+
+            // 更新委托人信息
+            BeanUtils.copyProperties(dto, entrustUser);
+            entrustUser.setId(id);
+            
+            return this.updateById(entrustUser);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserServiceImpl.updateEntrustUser error: {}", e.getMessage(), e);
+            throw new RuntimeException("更新委托人信息失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 删除委托人信息(逻辑删除)
+     *
+     * @param id 委托人ID
+     * @return 是否删除成功
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteEntrustUser(Integer id) {
+        log.info("SecondEntrustUserServiceImpl.deleteEntrustUser id={}", id);
+        try {
+            SecondEntrustUser entrustUser = this.getById(id);
+            if (entrustUser == null) {
+                log.warn("委托人信息不存在, id={}", id);
+                throw new RuntimeException("委托人信息不存在");
+            }
+            return this.removeById(id);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserServiceImpl.deleteEntrustUser error: {}", e.getMessage(), e);
+            throw new RuntimeException("删除委托人信息失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据用户电话查询委托人信息列表
+     *
+     * @param entrustUserPhone 用户电话
+     * @return 委托人信息列表
+     */
+    @Override
+    public List<SecondEntrustUser> getByUserPhone(String entrustUserPhone) {
+        log.info("SecondEntrustUserServiceImpl.getByUserPhone entrustUserPhone={}", entrustUserPhone);
+        LambdaQueryWrapper<SecondEntrustUser> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SecondEntrustUser::getEntrustUserPhone, entrustUserPhone);
+        wrapper.orderByDesc(SecondEntrustUser::getCreatedTime);
+        return this.list(wrapper);
+    }
+
+    /**
+     * 分页查询委托人信息列表(以姓名+身份证号为维度)
+     *
+     * @param queryVo 查询条件
+     * @return 委托人信息分页列表
+     */
+    @Override
+    public IPage<SecondEntrustUserResultVo> getEntrustUserPage(SecondEntrustUserQueryVo queryVo) {
+        log.info("SecondEntrustUserServiceImpl.getEntrustUserPage queryVo={}", queryVo);
+        try {
+            // 创建分页对象
+            Page<SecondEntrustUser> page = new Page<>(queryVo.getPageNum(), queryVo.getPageSize());
+            
+            // 构建查询条件(用于HAVING子句)
+            QueryWrapper<SecondEntrustUser> wrapper = new QueryWrapper<>();
+            
+            // 委托人姓名模糊查询
+            if (StringUtils.isNotBlank(queryVo.getEntrustUserName())) {
+                wrapper.apply("entrust.entrust_user_name LIKE CONCAT('%', {0}, '%')", queryVo.getEntrustUserName());
+            }
+            
+            // 身份证号模糊查询
+            if (StringUtils.isNotBlank(queryVo.getEntrustIdCard())) {
+                wrapper.apply("entrust.entrust_id_card LIKE CONCAT('%', {0}, '%')", queryVo.getEntrustIdCard());
+            }
+            
+            // 委托人电话模糊查询
+            if (StringUtils.isNotBlank(queryVo.getEntrustUserPhone())) {
+                wrapper.apply("entrust.entrust_user_phone LIKE CONCAT('%', {0}, '%')", queryVo.getEntrustUserPhone());
+            }
+            
+            // 交易编号模糊查询
+            if (StringUtils.isNotBlank(queryVo.getEntrustTradeNo())) {
+                wrapper.apply("trade.trade_no LIKE CONCAT('%', {0}, '%')", queryVo.getEntrustTradeNo());
+            }
+            // deleteFlag = 0
+            wrapper.eq("entrust.delete_flag", 0);
+            
+            // 执行分页查询
+            return secondEntrustUserMapper.getEntrustUserPage(page, wrapper);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserServiceImpl.getEntrustUserPage error: {}", e.getMessage(), e);
+            throw new RuntimeException("查询委托人信息列表失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据姓名和身份证号获取委托人详情(包含该人所有委托记录的交易信息)
+     *
+     * @param entrustUserName 委托人姓名
+     * @param entrustIdCard 委托人身份证号
+     * @return 委托人详情
+     */
+    @Override
+    public SecondEntrustUserDetailVo getEntrustUserDetail(String entrustUserName, String entrustIdCard) throws Exception {
+        log.info("SecondEntrustUserServiceImpl.getEntrustUserDetail entrustUserName={}, entrustIdCard={}", entrustUserName, entrustIdCard);
+        try {
+            // 1. 根据姓名和身份证号查询该人的所有委托记录
+            List<SecondEntrustUser> entrustUsers = secondEntrustUserMapper.getByUserNameAndIdCard(entrustUserName, entrustIdCard);
+            
+            if (entrustUsers == null || entrustUsers.isEmpty()) {
+                log.warn("委托人信息不存在, entrustUserName={}, entrustIdCard={}", entrustUserName, entrustIdCard);
+                throw new RuntimeException("委托人信息不存在");
+            }
+            
+            // 2. 创建返回对象,使用第一条记录作为基本信息(同一个人的基本信息相同)
+            SecondEntrustUserDetailVo detailVo = new SecondEntrustUserDetailVo();
+            SecondEntrustUser firstRecord = entrustUsers.get(0);
+            detailVo.setEntrustUserInfo(firstRecord);
+            
+            // 3. 收集该人所有委托记录关联的交易ID
+            List<Integer> tradeIds = new ArrayList<>();
+            for (SecondEntrustUser entrustUser : entrustUsers) {
+                if (entrustUser.getEntrustTradeId() != null) {
+                    tradeIds.add(entrustUser.getEntrustTradeId());
+                }
+            }
+            
+            // 4. 查询所有关联的交易记录
+            List<SecondTradeRecordVo> tradeRecordVos = new ArrayList<>();
+            
+            if (!tradeIds.isEmpty()) {
+                // 去重
+                tradeIds = tradeIds.stream().distinct().collect(java.util.stream.Collectors.toList());
+                
+                // 批量查询交易记录
+                QueryWrapper<SecondTradeRecord> tradeWrapper = new QueryWrapper<>();
+                tradeWrapper.in("id", tradeIds)
+                        .eq("delete_flag", 0)
+                        .orderByDesc("created_time");
+                
+                List<SecondTradeRecord> tradeRecords = secondTradeRecordMapper.selectList(tradeWrapper);
+                
+                // 5. 处理每条交易记录,添加操作节点信息和用户信息
+                if (tradeRecords != null && !tradeRecords.isEmpty()) {
+                    for (SecondTradeRecord tradeRecord : tradeRecords) {
+                        // 获取完整的交易记录信息(包含买卖双方信息)
+                        SecondTradeRecordVo tradeRecordVo = secondTradeRecordMapper.getTradeRecordById(tradeRecord.getId());
+                        
+                        if (tradeRecordVo != null) {
+                            // 添加交易操作节点信息
+                            List<JSONObject> operationJsonList = platformSecondTradeService.getOperationJsonList(tradeRecord.getId());
+                            tradeRecordVo.setOperationJsonList(operationJsonList);
+                            
+                            tradeRecordVos.add(tradeRecordVo);
+                        }
+                    }
+                }
+            }
+            
+            detailVo.setTradeRecords(tradeRecordVos);
+            
+            return detailVo;
+        } catch (Exception e) {
+            log.error("SecondEntrustUserServiceImpl.getEntrustUserDetail error: {}", e.getMessage(), e);
+            throw new Exception("获取委托人详情失败: " + e.getMessage());
+        }
+    }
+
+}
+

+ 769 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsAuditServiceImpl.java

@@ -0,0 +1,769 @@
+package shop.alien.second.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.google.common.collect.Lists;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.SecondVideoTask;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.SecondGoodsAudit;
+import shop.alien.entity.second.SecondGoodsRecord;
+import shop.alien.entity.second.enums.SecondGoodsStatusEnum;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.entity.store.SecondAiTask;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.mapper.SecondAiTaskMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.second.SecondGoodsAuditMapper;
+import shop.alien.mapper.second.SecondGoodsMapper;
+import shop.alien.mapper.second.SecondGoodsRecordMapper;
+import shop.alien.second.service.*;
+import shop.alien.second.util.AiTaskUtils;
+import shop.alien.util.common.Constants;
+import shop.alien.util.common.safe.ImageModerationResultVO;
+import shop.alien.util.common.safe.ImageModerationUtil;
+import shop.alien.util.common.safe.ImageReviewServiceEnum;
+import shop.alien.util.common.safe.TextModerationResultVO;
+import shop.alien.util.common.safe.TextModerationUtil;
+import shop.alien.util.common.safe.TextReviewServiceEnum;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 二手商品审核服务实现类
+ * 负责商品的图片、文本、视频审核相关业务逻辑
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondGoodsAuditServiceImpl implements SecondGoodsAuditService {
+
+    private final SecondAiTaskMapper secondAiTaskMapper;
+    private final StoreImgMapper storeImgMapper;
+    /**
+     * 视频审核功能是否启用的配置项
+     */
+    @Value("${video.moderation.enabled}")
+    private boolean videoModerationEnabled;
+
+    /**
+     * 视频审核失败时是否阻塞商品发布的配置项
+     */
+    @Value("${video.moderation.block-on-failure}")
+    private boolean videoModerationBlockOnFailure;
+
+    /**
+     * AI审核结果查询接口地址
+     */
+    @Value("${audit.result-url:http://192.168.2.250:9100/ai/auto-review/api/v1/audit_task/getResult}")
+    private String aiAuditResultUrl;
+
+    /**
+     * 视频审核服务,用于处理商品中视频内容的审核
+     */
+    private final VideoModerationService videoModerationService;
+
+    /**
+     * 文本审核工具,用于审核商品标题、描述等文本内容
+     */
+    private final TextModerationUtil textModerationUtil;
+
+    /**
+     * 图片审核工具,用于审核商品图片内容
+     */
+    private final ImageModerationUtil imageModerationUtil;
+
+    /**
+     * 二手商品Mapper
+     */
+    private final SecondGoodsMapper secondGoodsMapper;
+
+    /**
+     * 二手商品审核Mapper
+     */
+    private final SecondGoodsAuditMapper secondGoodsAuditMapper;
+
+    /**
+     * 商品操作历史记录Mapper
+     */
+    private final SecondGoodsRecordMapper secondGoodsRecordMapper;
+
+    /**
+     * 消息通知服务
+     */
+    private final SecondGoodsNotificationService notificationService;
+
+    /**
+     * 操作历史记录服务
+     */
+    private final SecondGoodsOperationRecordService operationRecordService;
+
+    private final AiTaskUtils aiTaskUtil;
+
+    @Lazy
+    @Autowired
+    private SecondGoodsService secondGoodsService;
+
+
+
+    /**
+     * 执行内容审核
+     * @param goodsDTO 商品信息
+     * @param goods 商品实体
+     */
+    @Override
+    public void performContentReview(SecondGoodsVo goodsDTO, SecondGoods goods) throws Exception {
+        // 图片审核
+        boolean imageAuditResult = performImageReviews(goods, goodsDTO);
+        
+        // 审核失败,直接返回
+        if (!imageAuditResult) {
+            // 图片审核不通过,记录操作历史
+            operationRecordService.recordGoodsOperation(goods, "图片审核失败");
+            return;
+        }
+
+        // 文本审核
+        boolean textAuditResult = performTextReview(goods, goodsDTO);
+        
+        // 审核失败,直接返回
+        if (!textAuditResult) {
+            // 文本审核不通过,记录操作历史
+            operationRecordService.recordGoodsOperation(goods, "文本审核失败");
+            return;
+        }
+        
+        // 视频审核
+        List<String> taskIds = performVideoReviews(goods, goodsDTO);
+
+        // 如果成功提交了视频审核任务,设置商品状态为审核中
+        if (!taskIds.isEmpty()) {
+            goods.setGoodsStatus(SecondGoodsStatusEnum.UNDER_REVIEW.getCode()); // 审核中
+            goods.setVideoTaskId(taskIds.get(0)); // 保存第一个任务ID到商品表
+            goods.setFailedReason("");
+            secondGoodsMapper.updateById(goods);
+            // 审核中审核记录
+            createGoodsAudit(goods, "", Constants.AuditStatus.UNDER_REVIEW);
+        } else {
+            // 第二轮AI审核
+            boolean b = performSecondRoundReview(goods, goodsDTO);
+            if (!b) {
+                // 第二轮审核失败,记录操作历史
+                operationRecordService.recordGoodsOperation(goods, "第二轮审核失败");
+                goods.setGoodsStatus(2);
+                goods.setFailedReason("调用AI接口失败,未获取到task_id");
+                secondGoodsMapper.updateById(goods);
+            }
+        }
+        
+        // 审核通过后上架商品
+//        approveAndListGoods(goods);
+    }
+
+    // 第二轮审核(AI)
+    @Override
+    public boolean performSecondRoundReview(SecondGoods goods, SecondGoodsVo goodsDTO) {
+        try {
+            // 参数校验
+            if (goodsDTO == null || CollectionUtil.isEmpty(goodsDTO.getImgUrl())) {
+                log.warn("Second round review skipped: empty image URLs for goods id={}",
+                        goods != null ? goods.getId() : "unknown");
+                return false;
+            }
+
+            // 获取访问令牌
+            String accessToken = aiTaskUtil.getAccessToken();
+            if (StringUtils.isEmpty(accessToken)) {
+                log.error("Failed to obtain access token for second round review, goods id={}", goods.getId());
+                return false;
+            }
+
+            // 创建AI任务
+            String test = goodsDTO.getDescription() + goodsDTO.getTitle() + goods.getLabel() + goods.getTopic();
+            String taskId = aiTaskUtil.createTask(accessToken, test, goodsDTO.getImgUrl());
+            if (StringUtils.isEmpty(taskId)) {
+                log.warn("Failed to create AI task for second round review, goods id={}", goods.getId());
+                return false;
+            }
+            goods.setAiTaskId(taskId);
+            goods.setGoodsStatus(SecondGoodsStatusEnum.UNDER_REVIEW.getCode());
+            secondGoodsMapper.updateById(goods);
+
+            // 保存任务信息
+            SecondAiTask secondAiTask = new SecondAiTask();
+            secondAiTask.setTaskId(taskId);
+            secondAiTask.setStatus("PROCESSING");
+            Date currentTime = new Date();
+            secondAiTask.setCreateTime(currentTime);
+            secondAiTask.setUpdateTime(currentTime);
+            secondAiTaskMapper.insert(secondAiTask);
+
+            log.info("Successfully created second round AI review task, taskId={}, goodsId={}",
+                    taskId, goods.getId());
+            return true;
+        } catch (Exception e) {
+            log.error("Error during second round review for goods id={}",
+                    goods != null ? goods.getId() : "unknown", e);
+            return false;
+        }
+    }
+
+
+    /**
+     * 执行图片审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    @Override
+    public boolean performImageReviews(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception {
+        // 图片审核(循环处理)
+        List<String> imageUrls = goodsDTO.getImgUrl();
+        // 根据imageUrls过滤不是图片的url
+        imageUrls = imageUrls.stream()
+                .filter(url -> url.toLowerCase().endsWith(".jpg") 
+                        || url.toLowerCase().endsWith(".png") 
+                        || url.toLowerCase().endsWith(".jpeg") 
+                        || url.toLowerCase().endsWith(".gif"))
+                .collect(Collectors.toList());
+        
+        // 图片审核
+        if (imageUrls != null && !imageUrls.isEmpty()) {
+            for (String imageUrl : imageUrls) {
+                List<String> imgServicesList = Lists.newArrayList();
+                // 内容治理检测 + AIGC图片风险检测
+                imgServicesList.add(ImageReviewServiceEnum.TONALITY_IMPROVE.getService());
+                imgServicesList.add(ImageReviewServiceEnum.AIGC_CHECK.getService());
+                
+                ImageModerationResultVO response = imageModerationUtil.productPublishCheck(imageUrl, imgServicesList);
+                if ("high".equals(response.getRiskLevel())) {
+                    // 图片审核不通过或存在高风险
+                    goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode()); // 审核失败
+                    String failReason = "图片审核不通过:图片中包含" + 
+                            (response.getDescriptions() != null ? response.getDescriptions() : "高风险内容");
+                    goods.setFailedReason(failReason);
+                    // 插入审核记录
+                    createGoodsAudit(goods, failReason, Constants.AuditStatus.FAILED);
+                    // 发送审核失败消息
+                    notificationService.sendFailedMsg(goods);
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 执行文本审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    @Override
+    public boolean performTextReview(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception {
+        List<String> servicesList = Lists.newArrayList();
+        servicesList.add(TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService());
+        servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
+        
+        // 使用商品发布场景的审核服务
+        String test = goodsDTO.getDescription() + goodsDTO.getTitle() + goods.getLabel() + goods.getTopic();
+        TextModerationResultVO textCheckResult = textModerationUtil.invokeFunction(test, servicesList);
+
+        if ("high".equals(textCheckResult.getRiskLevel())) {
+            // 文本审核不通过或存在高风险
+            goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode()); // 审核失败
+            String failReason = "文本审核不通过:" + 
+                    (textCheckResult.getRiskWords() != null ? textCheckResult.getRiskWords() : "存在高风险内容");
+            goods.setFailedReason(failReason);
+            // 插入审核记录
+            createGoodsAudit(goods, failReason, Constants.AuditStatus.FAILED);
+            // 发送审核失败消息
+            notificationService.sendFailedMsg(goods);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 执行视频审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    @Override
+    public List<String> performVideoReviews(SecondGoods goods, SecondGoodsVo goodsDTO) {
+        List<String> videoUrls = extractVideoUrls(goodsDTO.getImgUrl());
+        List<String> taskIds = new ArrayList<>();
+        
+        // 视频审核
+        if (videoModerationEnabled) {
+            if (!videoUrls.isEmpty()) {
+                // 提交视频审核任务
+                for (String videoUrl : videoUrls) {
+                    try {
+                        String taskId = videoModerationService.submitVideoModerationTask(videoUrl);
+                        taskIds.add(taskId);
+                    } catch (Exception e) {
+                        log.error("提交视频审核任务失败,视频URL: {}", videoUrl, e);
+                        if (videoModerationBlockOnFailure) {
+                            // 视频审核提交失败,设置为审核失败状态
+                            goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+                            goods.setFailedReason("视频审核提交失败: " + e.getMessage());
+                            createGoodsAudit(goods, "视频审核提交失败", Constants.AuditStatus.FAILED);
+                            notificationService.sendFailedMsg(goods);
+                        }
+                    }
+                }
+            }
+        }
+        return taskIds;
+    }
+
+    /**
+     * 处理视频审核结果
+     * @param task 视频审核任务
+     */
+    @Override
+    public void processVideoModerationResult(SecondVideoTask task) {
+        try {
+            // 查找关联的商品
+            QueryWrapper<SecondGoods> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("video_task_id", task.getTaskId());
+            SecondGoods goods = secondGoodsMapper.selectOne(queryWrapper);
+            
+            if (goods == null) {
+                log.warn("未找到关联的商品,任务ID: {}", task.getTaskId());
+                return;
+            }
+
+
+            // 根据审核结果更新商品状态
+            if ("none".equals(task.getRiskLevel())) {
+                QueryWrapper<StoreImg> imgQueryWrapper = new QueryWrapper<>();
+                imgQueryWrapper.eq("store_id", goods.getId());
+                imgQueryWrapper.eq("img_type", 19);
+                List<StoreImg> storeImgs = storeImgMapper.selectList(imgQueryWrapper);
+                List<String> imgUrls = storeImgs.stream()
+                        .map(StoreImg::getImgUrl)
+                        .filter(imgUrl -> StringUtils.hasText(imgUrl))
+                        .collect(Collectors.toList());
+
+                SecondGoodsVo goodsDTO = new SecondGoodsVo();
+                goodsDTO.setImgUrl(imgUrls);
+
+                // 开始第二轮审核
+                boolean b = performSecondRoundReview(goods, goodsDTO);
+
+                // 审核通过
+//                approveAndListGoods(goods);
+            } else {
+                // 审核不通过
+                goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+                
+                // 解析审核结果,生成具体的失败原因
+                String failedReason = parseVideoModerationFailureReason(task);
+                goods.setFailedReason(failedReason);
+                secondGoodsMapper.updateById(goods);
+                
+                // 更新审核记录
+                createGoodsAudit(goods, failedReason, Constants.AuditStatus.FAILED);
+
+                // 记录操作历史
+                operationRecordService.recordGoodsOperation(goods, "视频审核失败");
+                // 发送审核失败消息
+                notificationService.sendFailedMsg(goods);
+            }
+        } catch (Exception e) {
+            log.error("处理视频审核结果时发生异常,任务ID: {}", task.getTaskId(), e);
+        }
+    }
+
+    /**
+     * 审核通过后上架商品
+     * @param goods 商品信息
+     */
+    @Override
+    public void approveAndListGoods(SecondGoods goods) {
+        boolean isNotified = false;
+        try {
+            // 如果所有审核都通过,设置为上架状态
+            goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode()); // 上架
+            goods.setFailedReason("");
+            goods.setReleaseTime(new Date()); // 上架时间
+            secondGoodsMapper.updateById(goods);
+            // 插入审核记录
+            createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+            // 发送审核成功消息
+            notificationService.sendMessage(goods);
+            isNotified = true; // 标记通知已发送
+            
+            // 上架 记录商品操作历史
+            String operationName = "";
+            QueryWrapper<SecondGoodsRecord> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("goods_id", goods.getId());
+            List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryWrapper);
+            if (CollectionUtil.isNotEmpty(recordList)) {
+                operationName = "重新发布";
+            } else {
+                operationName = "首次发布";
+            }
+            operationRecordService.recordGoodsOperation(goods, operationName);
+
+            // 审核通过
+//                goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode());
+//                goods.setFailedReason("");
+//                goods.setReleaseTime(new Date());
+//                updateById(goods);
+//
+//                // 更新审核记录
+//                createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+//
+//                // 发送审核成功消息
+//                sendMessage(goods);
+//                // 审核成功,记录操作历史
+//                // 审核成功,记录操作历史
+//                String operationName = "";
+//                QueryWrapper<SecondGoodsRecord> queryRecordWrapper = new QueryWrapper<>();
+//                queryRecordWrapper.eq("goods_id", goods.getId());
+//                log.info("查询操作记录开始 goods_id: {}", goods.getId());
+//                List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryRecordWrapper);
+//                log.info("查询操作记录结束 recordList: {}", recordList);
+//                if (CollectionUtil.isNotEmpty(recordList)){
+//                    operationName = "重新发布";
+//                }else {
+//                    operationName = "首次发布";
+//                }
+//                recordGoodsOperation(goods, operationName);
+//
+//                // 检查用户是否在24小时内发布同类商品超过阈值
+//                if (!checkUserPublishSameCategoryLimit(goods)) {
+//                    log.warn("用户 {} 在24小时内发布同类商品次数超过限制", goods.getUserId());
+//                }
+//
+//                // 检查用户是否在24小时内发布商品超过阈值
+//                if (!checkUserPublishLimit(goods)) {
+//                    log.warn("用户 {} 在24小时内发布商品次数超过限制", goods.getUserId());
+//
+//                }
+        } catch (Exception e) {
+            log.error("商品上架过程中发生异常,执行回滚", e);
+            // 如果通知已发送但后续操作失败,需要补偿
+            if (isNotified) {
+                // 发送补偿消息,比如撤销通知或标记为异常状态
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * 创建商品审核记录
+     * @param goods 商品信息
+     * @param failReason 审核失败原因
+     * @param goodsStatus 商品状态
+     */
+    @Override
+    public void createGoodsAudit(SecondGoods goods, String failReason, Integer goodsStatus) {
+        // 保存审核结果
+        secondGoodsMapper.updateById(goods);
+        // 插入审核记录
+        SecondGoodsAudit auditRecord = new SecondGoodsAudit();
+        auditRecord.setGoodsId(goods.getId());
+        auditRecord.setGoodsStatus(goodsStatus); // 审核状态
+        if (Constants.AuditStatus.FAILED.equals(goodsStatus)) {
+            auditRecord.setFailedReason(failReason);
+        }
+        auditRecord.setCreatedUserId(goods.getUserId());
+        auditRecord.setUpdatedUserId(goods.getUserId());
+        auditRecord.setCreatedTime(new Date());
+        auditRecord.setUpdatedTime(new Date());
+        secondGoodsAuditMapper.insert(auditRecord);
+        goods.setAuditRecordId(auditRecord.getId());
+    }
+
+    /**
+     * 从图片URL列表中提取视频URL
+     * @param imageUrls 图片URL列表
+     * @return 视频URL列表
+     */
+    @Override
+    public List<String> extractVideoUrls(List<String> imageUrls) {
+        if (CollectionUtil.isEmpty(imageUrls)) {
+            return Collections.emptyList();
+        }
+        
+        List<String> videoUrls = new ArrayList<>();
+        for (String url : imageUrls) {
+            if (isVideoUrl(url)) {
+                videoUrls.add(url);
+            }
+        }
+        return videoUrls;
+    }
+
+    /**
+     * 判断URL是否为视频地址
+     * @param url 输入URL
+     * @return 是否为视频地址
+     */
+    @Override
+    public boolean isVideoUrl(String url) {
+        if (url == null) return false;
+        url = url.toLowerCase();
+        return url.endsWith(".mp4") ||
+                url.endsWith(".avi") ||
+                url.endsWith(".mov") ||
+                url.endsWith(".flv") ||
+                url.endsWith(".wmv") ||
+                url.endsWith(".mkv");
+    }
+
+    /**
+     * 解析视频审核失败原因
+     * @param task 视频审核任务
+     * @return 失败原因
+     */
+    private String parseVideoModerationFailureReason(SecondVideoTask task) {
+        StringBuilder reasonBuilder = new StringBuilder("视频审核不通过,风险等级: " + task.getRiskLevel());
+        
+        try {
+            // 解析审核结果JSON
+            JSONObject resultJson = JSON.parseObject(task.getResult());
+            if (resultJson != null && resultJson.containsKey("data")) {
+                JSONObject data = resultJson.getJSONObject("data");
+                
+                // 处理帧结果(视频画面)
+                if (data.containsKey("FrameResult")) {
+                    JSONObject frameResult = data.getJSONObject("FrameResult");
+                    if (frameResult != null && frameResult.containsKey("FrameSummarys")) {
+                        JSONArray frameSummarys = frameResult.getJSONArray("FrameSummarys");
+                        if (frameSummarys != null && !frameSummarys.isEmpty()) {
+                            reasonBuilder.append("。检测到违规内容:");
+                            for (int i = 0; i < frameSummarys.size(); i++) {
+                                JSONObject summary = frameSummarys.getJSONObject(i);
+                                String label = summary.getString("Label");
+                                String description = summary.getString("Description");
+                                Integer labelSum = summary.getInteger("LabelSum");
+                                
+                                if (label != null && !label.isEmpty()) {
+                                    reasonBuilder.append("[").append(description != null ? description : label)
+                                            .append("]出现").append(labelSum != null ? labelSum : 1).append("次;");
+                                }
+                            }
+                        }
+                    }
+                }
+                
+                // 处理音频结果
+                if (data.containsKey("AudioResult")) {
+                    JSONObject audioResult = data.getJSONObject("AudioResult");
+                    if (audioResult != null && audioResult.containsKey("AudioSummarys")) {
+                        JSONArray audioSummarys = audioResult.getJSONArray("AudioSummarys");
+                        if (audioSummarys != null && !audioSummarys.isEmpty()) {
+                            reasonBuilder.append("。检测到违规音频:");
+                            for (int i = 0; i < audioSummarys.size(); i++) {
+                                JSONObject summary = audioSummarys.getJSONObject(i);
+                                String label = summary.getString("Label");
+                                String description = summary.getString("Description");
+                                Integer labelSum = summary.getInteger("LabelSum");
+                                
+                                if (label != null && !label.isEmpty()) {
+                                    reasonBuilder.append("[").append(description != null ? description : label)
+                                            .append("]出现").append(labelSum != null ? labelSum : 1).append("次;");
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.warn("解析视频审核结果失败,使用默认原因,任务ID: {}", task.getTaskId(), e);
+        }
+        
+        return reasonBuilder.toString();
+    }
+
+    /**
+     * 获取AI商品审核结果
+     * 查询所有状态为处理中的AI审核任务,并更新审核结果
+     * @return 处理结果
+     */
+    @Override
+    public String getAiGoodsCheckResult() {
+        // 获取AI服务Token
+        String accessToken = aiTaskUtil.getAccessToken();
+        if (!StringUtils.hasText(accessToken)) {
+            log.error("调用AI服务登录接口失败,无法获取token");
+            return "调用AI服务登录接口失败";
+        }
+
+        // 查询所有状态为处理中的任务
+        List<SecondAiTask> pendingTasks = secondAiTaskMapper.selectList(
+                new QueryWrapper<SecondAiTask>().eq("status", "PROCESSING")
+        );
+
+        if (CollectionUtil.isEmpty(pendingTasks)) {
+            log.info("没有处理中的AI审核任务");
+            return "没有处理中的AI审核任务";
+        }
+
+        int passCount = 0;
+        int rejectCount = 0;
+        int pendingCount = 0;
+        int failCount = 0;
+
+        for (SecondAiTask task : pendingTasks) {
+            String result = processAiTaskResult(task, accessToken);
+            switch (result) {
+                case "success": passCount++; break;
+                case "reject": rejectCount++; break;
+                case "pending": pendingCount++; break;
+                default: failCount++;
+            }
+        }
+
+        return String.format("AI审核结果处理完成,通过:%d,拒绝:%d,处理中:%d,异常:%d", passCount, rejectCount, pendingCount, failCount);
+    }
+
+    /**
+     * 处理单个AI任务结果
+     * @param task AI任务
+     * @param accessToken 访问令牌
+     * @return success-审核通过, reject-审核拒绝, pending-处理中, fail-处理失败
+     */
+    private String processAiTaskResult(SecondAiTask task, String accessToken) {
+        String resultUrl = aiAuditResultUrl + "?task_id=" + task.getTaskId();
+        try {
+            org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
+            headers.set("Authorization", "Bearer " + accessToken);
+
+            org.springframework.web.client.RestTemplate restTemplate = new org.springframework.web.client.RestTemplate();
+            org.springframework.http.ResponseEntity<String> response = restTemplate.exchange(
+                    resultUrl, org.springframework.http.HttpMethod.GET,
+                    new org.springframework.http.HttpEntity<>(headers), String.class);
+
+            if (response.getStatusCodeValue() != 200 || response.getBody() == null) {
+                log.error("调用AI审核结果接口失败,taskId: {}, http状态: {}", task.getTaskId(), response.getStatusCode());
+                return "fail";
+            }
+
+            JSONObject dataJson = JSONObject.parseObject(response.getBody()).getJSONObject("data");
+            if (dataJson == null) {
+                log.error("AI审核返回数据为空,taskId: {}", task.getTaskId());
+                return "fail";
+            }
+
+            String status = dataJson.getString("status");
+            // 任务仍在处理中
+            if ("pending".equals(status)) {
+                return "pending";
+            }
+
+            // 任务已完成
+            if ("done".equals(status)) {
+                // 更新任务状态和结果
+                task.setStatus("SUCCESS");
+                task.setResult(dataJson.toJSONString());
+                task.setUpdateTime(new Date());
+                secondAiTaskMapper.updateById(task);
+
+                // 查询关联商品
+                SecondGoods goods = secondGoodsMapper.selectOne(
+                        new QueryWrapper<SecondGoods>().eq("ai_task_id", task.getTaskId()));
+                if (goods == null) {
+                    log.error("商品不存在,taskId: {}", task.getTaskId());
+                    return "fail";
+                }
+
+                String result = dataJson.getString("result");
+                if ("pass".equals(result)) {
+                    // 审核通过,上架商品
+                    log.info("AI审核通过,商品ID: {}, taskId: {}", goods.getId(), task.getTaskId());
+                    handleGoodsApprovalSuccess(goods);
+                    return "success";
+                } else if ("reject".equals(result) || "suspect".equals(result)) {
+                    // 审核拒绝,处理失败流程
+                    String reason = dataJson.getString("reason");
+                    String violation = dataJson.getString("violation");
+                    String ruleHit = dataJson.getString("rule_hit");
+
+                    // 构建失败原因
+                    StringBuilder failReason = new StringBuilder("AI审核不通过");
+                    if (StringUtils.hasText(reason)) {
+                        failReason.append(":").append(reason);
+                    }
+                    if (StringUtils.hasText(violation)) {
+                        failReason.append(",违规类型:").append(violation);
+                    }
+                    if (StringUtils.hasText(ruleHit)) {
+                        failReason.append(",命中规则:").append(ruleHit);
+                    }
+
+                    log.info("AI审核拒绝,商品ID: {}, taskId: {}, 原因: {}", goods.getId(), task.getTaskId(), failReason);
+
+                    // 更新商品状态为审核失败
+                    goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+                    goods.setFailedReason(failReason.toString());
+                    secondGoodsMapper.updateById(goods);
+
+                    // 创建审核失败记录
+                    createGoodsAudit(goods, failReason.toString(), Constants.AuditStatus.FAILED);
+
+                    // 记录操作历史
+                    operationRecordService.recordGoodsOperation(goods, "AI审核失败");
+
+                    // 发送审核失败消息通知
+                    notificationService.sendFailedMsg(goods);
+
+                    return "reject";
+                }
+            }
+            return "fail";
+        } catch (Exception e) {
+            log.error("处理AI审核任务结果异常,taskId: {}", task.getTaskId(), e);
+            return "fail";
+        }
+    }
+
+    /**
+     * 商品审核通过后的上架处理(包含风控检查)
+     * @param goods 商品信息
+     */
+    @Override
+    public void handleGoodsApprovalSuccess(SecondGoods goods) {
+        // 审核通过,设置上架状态
+        goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode());
+        goods.setFailedReason("");
+        goods.setReleaseTime(new Date());
+        secondGoodsMapper.updateById(goods);
+
+        // 更新审核记录
+        createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+
+        // 发送审核成功消息
+        notificationService.sendMessage(goods);
+
+        // 记录操作历史
+        QueryWrapper<SecondGoodsRecord> queryRecordWrapper = new QueryWrapper<>();
+        queryRecordWrapper.eq("goods_id", goods.getId());
+        List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryRecordWrapper);
+        String operationName = CollectionUtil.isNotEmpty(recordList) ? "重新发布" : "首次发布";
+        operationRecordService.recordGoodsOperation(goods, operationName);
+
+        // 执行风控检查
+        secondGoodsService.performPublishRiskCheck(goods);
+    }
+}
+

+ 153 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsCategoryServiceImpl.java

@@ -0,0 +1,153 @@
+package shop.alien.second.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.second.SecondGoodsCategory;
+import shop.alien.entity.store.LifeFans;
+import shop.alien.mapper.second.SecondGoodsCategoryMapper;
+import shop.alien.second.service.SecondGoodsCategoryService;
+import shop.alien.util.common.JwtUtil;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 二手商品服务实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondGoodsCategoryServiceImpl extends ServiceImpl<SecondGoodsCategoryMapper, SecondGoodsCategory> implements SecondGoodsCategoryService {
+
+    @Autowired
+    private SecondGoodsCategoryMapper mapper;
+    /**
+     * 保存商品为草稿状态
+     * @param parentId 商品实体
+     * @return 是否成功保存
+     */
+    @Override
+    public List<SecondGoodsCategory> querySecondGoodsByParentId(Integer parentId, Integer categoryState) throws Exception {
+        try {
+            return mapper.querySecondGoodsByParentId(parentId, categoryState);
+        } catch (Exception e) {
+            log.error("SecondGoodsCategoryServiceImpl.querySecondGoodsByParentId Error Mgs={}", e.getMessage());
+            throw new Exception("查询二级商品分类失败", e);
+        }
+    }
+
+    @Override
+    public Integer insertSecondGoodsCategory(SecondGoodsCategory category) throws Exception {
+        try {
+            QueryWrapper<SecondGoodsCategory> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("parent_id", category.getParentId());
+            queryWrapper.orderByDesc("category_sort").last("LIMIT 1");
+            SecondGoodsCategory lastCategory = this.getOne(queryWrapper);
+
+            if(lastCategory == null || lastCategory.getCategorySort() == null) {
+                category.setCategorySort(1);
+            } else {
+                category.setCategorySort(lastCategory.getCategorySort() + 1);
+            }
+            return mapper.insert(category);
+        } catch (Exception e) {
+            log.error("SecondGoodsCategoryServiceImpl.insertSecondGoodsCategory Error Mgs={}", e.getMessage());
+            throw new Exception("查询二级商品分类失败", e);
+        }
+    }
+
+    public Integer updateSecondGoodsCategory(SecondGoodsCategory category) throws Exception {
+        try {
+            JSONObject data = JwtUtil.getCurrentUserInfo();
+            String userId = null;
+            if (data != null) {
+                userId = data.getString("userId");
+            }
+            if (StringUtil.isBlank(userId)) {
+                return null;
+            }
+            LambdaUpdateWrapper<SecondGoodsCategory> wrapper = new LambdaUpdateWrapper<>();
+            wrapper.eq(SecondGoodsCategory::getId, category.getId());
+            if (StringUtils.isNotBlank(category.getCategoryName())) {
+                wrapper.set(SecondGoodsCategory::getCategoryName, category.getCategoryName());
+            }
+            if (StringUtils.isNotBlank(category.getCategoryUrl())) {
+                wrapper.set(SecondGoodsCategory::getCategoryUrl, category.getCategoryUrl());
+            }
+            if (category.getParentId() != null) {
+                wrapper.set(SecondGoodsCategory::getParentId, category.getParentId());
+            }
+            if (category.getCategorySort() != null) {
+                wrapper.set(SecondGoodsCategory::getCategorySort, category.getCategorySort());
+            }
+            if (category.getCategoryState() != null) {
+                wrapper.set(SecondGoodsCategory::getCategoryState, category.getCategoryState());
+            }
+            wrapper.set(SecondGoodsCategory::getUpdatedUserId, userId);
+            if (category.getDeleteFlag() != null) {
+                wrapper.set(SecondGoodsCategory::getDeleteFlag, category.getDeleteFlag());
+            } else {
+                wrapper.set(SecondGoodsCategory::getDeleteFlag, 0);
+            }
+            return mapper.update(null, wrapper);
+        } catch (Exception e) {
+            log.error("SecondGoodsCategoryServiceImpl.insertSecondGoodsCategory Error Mgs={}", e.getMessage());
+            throw new Exception("更新二级商品分类失败", e);
+        }
+    }
+
+
+    @Override
+    public List<SecondGoodsCategory> querySecondGoodsTree() throws Exception {
+        try {
+            return buildTree(mapper.querySecondGoodsInfo());
+        } catch (Exception e) {
+            log.error("SecondGoodsCategoryServiceImpl.querySecondGoodsByParentId Error Mgs={}", e.getMessage());
+            throw new Exception("查询二级商品分类失败", e);
+        }
+    }
+
+    // 2. 构建树的核心逻辑
+    public List<SecondGoodsCategory> buildTree(List<SecondGoodsCategory> nodes) {
+        Map<Integer, SecondGoodsCategory> nodeMap = nodes.stream()
+                .collect(Collectors.toMap(SecondGoodsCategory::getId, n -> n));
+
+        List<SecondGoodsCategory> roots = nodes.stream()
+                .filter(n -> -1 == n.getParentId()) // 假设根节点的parentId为-1
+                .sorted((n1, n2) -> {
+                    if (n1.getCategorySort() == null) return 1;
+                    if (n2.getCategorySort() == null) return -1;
+                    return n1.getCategorySort().compareTo(n2.getCategorySort());
+                })
+                .collect(Collectors.toList());
+
+        for (SecondGoodsCategory root : roots) {
+            addChildrenRecursive(root, nodeMap);
+        }
+        return roots;
+    }
+
+    private void addChildrenRecursive(SecondGoodsCategory parent, Map<Integer, SecondGoodsCategory> nodeMap) {
+        List<SecondGoodsCategory> children = nodeMap.values().stream()
+                .filter(child -> child.getParentId().equals(parent.getId()))
+                .sorted((n1, n2) -> {
+                    if (n1.getCategorySort() == null) return 1;
+                    if (n2.getCategorySort() == null) return -1;
+                    return n1.getCategorySort().compareTo(n2.getCategorySort());
+                })
+                .collect(Collectors.toList());
+        if (!children.isEmpty()) {
+            parent.setChildren(children);
+        }
+        children.forEach(child -> addChildrenRecursive(child, nodeMap));
+    }
+}

+ 151 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsNotificationServiceImpl.java

@@ -0,0 +1,151 @@
+package shop.alien.second.service.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.store.LifeNotice;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.mapper.LifeUserMapper;
+import shop.alien.second.feign.AlienStoreFeign;
+import shop.alien.second.service.SecondGoodsNotificationService;
+import shop.alien.util.common.Constants;
+
+/**
+ * 二手商品消息通知服务实现类
+ * 负责商品相关的消息通知业务逻辑
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondGoodsNotificationServiceImpl implements SecondGoodsNotificationService {
+
+    /**
+     * 用户信息Mapper
+     */
+    private final LifeUserMapper lifeUserMapper;
+
+    /**
+     * 公告Mapper
+     */
+    private final LifeNoticeMapper lifeNoticeMapper;
+
+    /**
+     * 店铺服务Feign接口
+     */
+    private final AlienStoreFeign alienStoreFeign;
+
+    /**
+     * 发送审核成功消息
+     * @param goods 商品信息
+     */
+    @Override
+    public void sendMessage(SecondGoods goods) {
+        // 根据 goods.getUserId() 获取用户信息
+        LifeUser lifeUser = lifeUserMapper.selectById(goods.getUserId());
+        String phone = lifeUser.getUserPhone();
+        
+        // 调取feign接口 发送消息 life_notice表
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setSenderId("system");
+        lifeNotice.setReceiverId("user_" + phone);
+        lifeNotice.setBusinessId(goods.getAuditRecordId());
+        lifeNotice.setTitle("商品审核通知");
+        
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("goodsId", goods.getId());
+        jsonObject.put("status", "true");
+        jsonObject.put("message", "恭喜您的商品已发布成功。");
+        lifeNotice.setContext(jsonObject.toJSONString());
+        lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
+        lifeNotice.setIsRead(0);
+        lifeNoticeMapper.insert(lifeNotice);
+        
+        sendNotice("user_" + phone, lifeNotice);
+    }
+
+    /**
+     * 发送审核失败消息
+     * @param goods 商品信息
+     */
+    @Override
+    public void sendFailedMsg(SecondGoods goods) {
+        // 根据 goods.getUserId() 获取用户信息
+        LifeUser lifeUser = lifeUserMapper.selectById(goods.getUserId());
+        String phone = lifeUser.getUserPhone();
+        
+        // 调取feign接口 发送消息 life_notice表
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setSenderId("system");
+        lifeNotice.setReceiverId("user_" + phone);
+        lifeNotice.setBusinessId(goods.getAuditRecordId());
+        lifeNotice.setTitle("商品审核通知");
+
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("goodsId", goods.getId());
+        jsonObject.put("status", "false");
+        jsonObject.put("message", "抱歉您的商品发布失败,图片或文字存在违规行为,请您修改后重新发布。");
+        lifeNotice.setContext(jsonObject.toJSONString());
+        lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
+        lifeNotice.setIsRead(0);
+        lifeNoticeMapper.insert(lifeNotice);
+        
+        sendNotice("user_" + phone, lifeNotice);
+    }
+
+    /**
+     * 发送商品下架消息
+     * @param goods 商品信息
+     */
+    @Override
+    public void sendShelveMessage(SecondGoods goods) {
+        try {
+            // 根据 goods.getUserId() 获取用户信息
+            LifeUser lifeUser = lifeUserMapper.selectById(goods.getUserId());
+            String phone = lifeUser.getUserPhone();
+            
+            // 调取feign接口 发送消息 life_notice表
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setSenderId("system");
+            lifeNotice.setReceiverId("user_" + phone);
+            lifeNotice.setBusinessId(goods.getId());
+            lifeNotice.setTitle("商品下架通知");
+            
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("goodsId", goods.getId());
+            jsonObject.put("message", "您的商品:" + goods.getTitle() + " 已下架");
+            lifeNotice.setContext(jsonObject.toJSONString());
+            lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
+            lifeNotice.setIsRead(0);
+            lifeNoticeMapper.insert(lifeNotice);
+            
+            sendNotice("user_" + phone, lifeNotice);
+        } catch (Exception e) {
+            log.error("发送消息通知失败,goods: {}", goods, e);
+        }
+    }
+
+    /**
+     * 发送通知消息
+     * @param receiverId 接收者ID
+     * @param lifeNotice 通知内容
+     */
+    private void sendNotice(String receiverId, LifeNotice lifeNotice) {
+        try {
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId("system");
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+            alienStoreFeign.sendMsgToClientByPhoneId(receiverId, JSONObject.from(webSocketVo).toJSONString());
+        } catch (Exception e) {
+            log.error("发送消息通知失败,receiverId: {}", receiverId, e);
+        }
+    }
+}
+

+ 127 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsOperationRecordServiceImpl.java

@@ -0,0 +1,127 @@
+package shop.alien.second.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.SecondGoodsRecord;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.second.SecondGoodsRecordMapper;
+import shop.alien.second.service.SecondGoodsOperationRecordService;
+import shop.alien.util.common.Constants;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 二手商品操作历史记录服务实现类
+ * 负责记录商品的各种操作历史
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondGoodsOperationRecordServiceImpl implements SecondGoodsOperationRecordService {
+
+    /**
+     * 商品操作历史记录Mapper
+     */
+    private final SecondGoodsRecordMapper secondGoodsRecordMapper;
+
+    /**
+     * 店铺图片Mapper
+     */
+    private final StoreImgMapper storeImgMapper;
+
+    /**
+     * 记录商品操作历史
+     * @param goods 商品信息
+     * @param operationName 操作名称
+     */
+    @Override
+    public void recordGoodsOperation(SecondGoods goods, String operationName) {
+        try {
+            log.warn("开始创建操作历史: {},{} ", goods, operationName);
+            SecondGoodsRecord record = new SecondGoodsRecord();
+            record.setOperationName(operationName);
+            record.setGoodsId(goods.getId());
+            record.setUserId(goods.getUserId());
+            record.setTitle(goods.getTitle());
+            record.setDescription(goods.getDescription());
+            
+            // 价格转换
+            if (goods.getAmount() != null) {
+                record.setPrice(goods.getAmount());
+            } else if (goods.getPrice() != null && !goods.getPrice().isEmpty()) {
+                try {
+                    record.setPrice(new BigDecimal(goods.getPrice()));
+                } catch (Exception e) {
+                    log.warn("转换商品价格时出错: {}", e.getMessage());
+                }
+            }
+            
+            record.setPosition(goods.getPosition());
+            record.setLikeCount(goods.getLikeCount());
+            record.setCollectCount(goods.getCollectCount());
+            record.setCategoryOneId(goods.getCategoryOneId());
+            record.setCategoryTwoId(goods.getCategoryTwoId());
+            record.setLabel(goods.getLabel());
+            record.setTopic(goods.getTopic());
+            record.setTradeId(goods.getTradeId());
+            record.setReleaseTime(goods.getReleaseTime());
+            record.setGoodsStatus(goods.getGoodsStatus());
+            record.setFailedReason(goods.getFailedReason());
+            record.setHomeImage(goods.getHomeImage());
+            record.setVideoTaskId(goods.getVideoTaskId());
+            record.setVideoFirstFrame(goods.getVideoFirstFrame());
+            record.setDeleteFlag(goods.getDeleteFlag());
+            record.setCreatedTime(goods.getCreatedTime());
+            record.setCreatedUserId(goods.getCreatedUserId());
+            record.setUpdatedTime(goods.getUpdatedTime());
+            record.setUpdatedUserId(goods.getUpdatedUserId());
+            record.setAddressText(goods.getAddressText());
+            
+            secondGoodsRecordMapper.insert(record);
+            log.warn("创建操作历史结束: {} ", record);
+            
+            // 保存图片信息
+            saveRecordGoodsImages(record);
+        } catch (Exception e) {
+            log.error("记录商品操作历史时发生异常", e);
+        }
+    }
+
+    /**
+     * 保存二手商品记录图片类型信息
+     * @param record 保存后的商品记录
+     */
+    private void saveRecordGoodsImages(SecondGoodsRecord record) {
+        log.info("创建操作历图片史开始: {} ", record);
+        // 获取商品图片列表
+        QueryWrapper<StoreImg> query = new QueryWrapper<>();
+        query.lambda().eq(StoreImg::getStoreId, record.getGoodsId())
+                .eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_GOODS);
+        List<StoreImg> storeImgs = storeImgMapper.selectList(query);
+        
+        // 保存前先把原有的删除
+        storeImgs.forEach(storeImgModel -> {
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(record.getId());
+            storeImg.setImgType(Constants.ImageType.SECOND_HAND_RECORD);
+            storeImg.setImgSort(storeImgModel.getImgSort());
+            storeImg.setImgDescription("二手商品记录图片类型");
+            storeImg.setDeleteFlag(Constants.DeleteFlag.NOT_DELETED);
+            storeImg.setCreatedTime(new Date());
+            storeImg.setUpdatedTime(new Date());
+            storeImg.setCreatedUserId(1);
+            storeImg.setUpdatedUserId(1);
+            storeImg.setImgUrl(storeImgModel.getImgUrl());
+            // 保存图片 插入store_img数据库
+            storeImgMapper.insert(storeImg);
+            log.info("创建操作历图片结束: {} ", storeImg);
+        });
+    }
+}
+

+ 301 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsReportingServiceImpl.java

@@ -0,0 +1,301 @@
+package shop.alien.second.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.second.SecondGoodsRecord;
+import shop.alien.entity.second.vo.SecondReportingVo;
+import shop.alien.entity.second.vo.SecondUserViolationVo;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.mapper.*;
+import shop.alien.mapper.second.SecondGoodsRecordMapper;
+import shop.alien.second.feign.AlienStoreFeign;
+import shop.alien.second.service.SecondGoodsReportingService;
+import shop.alien.second.util.AiUserViolationUtils;
+import shop.alien.second.util.JsonUtils;
+import shop.alien.util.common.Constants;
+import shop.alien.util.common.JwtUtil;
+
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * 二手商品服务实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondGoodsReportingServiceImpl implements SecondGoodsReportingService {
+
+    List<String> videoFileType = Arrays.asList("mp4", "avi", "flv", "mkv", "rmvb", "wmv", "3gp", "mov");
+
+    @Autowired
+    private final LifeNoticeMapper lifeNoticeMapper;
+
+    @Autowired
+    private final LifeUserViolationMapper lifeUserViolationMapper;
+
+    @Autowired
+    private final StoreDictionaryMapper StoreDictionaryMapper;
+
+    @Autowired
+    private final LifeUserMapper lifeUserMapper;
+
+    @Autowired
+    private final SecondGoodsRecordMapper secondGoodsRecordMapper;
+
+    private final AlienStoreFeign alienStoreFeign;
+
+    @Autowired
+    private final StoreImgMapper storeImgMapper;
+
+    private final AiUserViolationUtils aiUserViolationUtils;
+
+    @Override
+    public SecondReportingVo queryReportingDetail (Integer id) {
+        SecondReportingVo secondReportingVo = new SecondReportingVo();
+
+        // 获取举报信息
+        LifeNotice lifeNotice = lifeNoticeMapper.selectById(id);
+
+        // 获取举报内容
+        LifeUserViolation lifeUserViolation = lifeUserViolationMapper.selectById(lifeNotice.getBusinessId());
+
+        // 获取举报类型
+        QueryWrapper<StoreDictionary> query = new QueryWrapper<>();
+        query.lambda()
+                .eq(StoreDictionary::getTypeName, lifeUserViolation.getDictType())
+                .eq(StoreDictionary::getDictId, lifeUserViolation.getDictId());
+        // 获取枚举
+        StoreDictionary storeDictionary =StoreDictionaryMapper.selectOne(query);
+
+        // 定义原始格式
+        DateTimeFormatter originalFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        // 定义目标格式
+        DateTimeFormatter targetFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
+        // 创建格式化器
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        // 执行格式化
+        String formattedDate = formatter.format(lifeNotice.getCreatedTime());
+        // 解析为 LocalDateTime 对象
+        LocalDateTime dateTime = LocalDateTime.parse(formattedDate, originalFormatter);
+
+        if (lifeUserViolation.getReportContextType().equals("4")) {
+            // 获取商品信息
+            SecondGoodsRecord secondGoods = secondGoodsRecordMapper.selectById(lifeUserViolation.getBusinessId());
+            secondReportingVo.setPrice(secondGoods.getPrice() != null ? secondGoods.getAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toString() : null);
+            secondReportingVo.setHomeImage(secondGoods.getHomeImage());
+            secondReportingVo.setTitle(secondGoods.getTitle());
+            secondReportingVo.setDescription(secondGoods.getDescription());
+            String context = "您于" + dateTime.format(targetFormatter) + "举报“" + secondGoods.getTitle() + "”“" + storeDictionary.getDictDetail() + "”举报内容为“"
+                    + lifeUserViolation.getOtherReasonContent()  + "”";
+            secondReportingVo.setReportingContext(context);
+        } else if (lifeUserViolation.getReportContextType().equals("5")) {
+            // 获取用户信息
+            LifeUser lifeUser = lifeUserMapper.selectById(lifeUserViolation.getReportedUserId());
+            String context = "您于" + dateTime.format(targetFormatter) + "举报“" + lifeUser.getUserName() + "”“" + storeDictionary.getDictDetail() + "”举报内容为“"
+                    + lifeUserViolation.getOtherReasonContent()  + "”";
+            secondReportingVo.setReportingContext(context);
+        } else if (lifeUserViolation.getReportContextType().equals("1") || lifeUserViolation.getReportContextType().equals("2") || lifeUserViolation.getReportContextType().equals("3")) {
+            // 用户信息
+            if (StringUtils.isNotBlank(lifeNotice.getContext())) {
+                String context = JsonUtils.getJsonValue(lifeNotice.getContext(), "message");
+                secondReportingVo.setReportingContext(context);
+            }
+        }
+        secondReportingVo.setFeedbackContext("平台已受理,感谢您的反馈!");
+        secondReportingVo.setReportingTime(lifeNotice.getCreatedTime());
+        secondReportingVo.setFeedbackTime(lifeNotice.getCreatedTime());
+        List<Map<String, Object>> list = new ArrayList<>();
+
+        // 存放已存在文件list
+        List<String> videoList = new ArrayList<>();
+
+        if (lifeUserViolation.getReportContextType().equals("1") || lifeUserViolation.getReportContextType().equals("2") || lifeUserViolation.getReportContextType().equals("3")) {
+            if (StringUtils.isNotBlank(lifeUserViolation.getReportEvidenceImg())) {
+                List<String> urlList = Arrays.asList(lifeUserViolation.getReportEvidenceImg().split(","));
+                for (int i = 0; i < urlList.size(); i++) {
+                    String url = urlList.get(i);
+                    // 过滤掉空的URL
+                    if (StringUtils.isBlank(url)) {
+                        continue;
+                    }
+
+                    Map<String, Object> map = new HashMap<>();
+
+                    // 查找最后一个点的位置
+                    int lastDotIndex = url.lastIndexOf('.');
+
+                    String fileType = url.substring(url.lastIndexOf(".") + 1);
+                    String contains = null;
+                    if (lastDotIndex != -1) { // 确保存在
+                        contains = url.substring(0, lastDotIndex);
+                    }
+
+                    if (!videoList.contains(contains)) {
+                        videoList.add(contains);
+                        if (videoFileType.contains(fileType.toLowerCase())) {
+                            map.put("type", "video");
+                            if (i + 1 < urlList.size() && StringUtils.isNotBlank(urlList.get(i + 1))) {
+                                map.put("imgUrl", urlList.get(i + 1));
+                                map.put("videoUrl", url);
+                                list.add(map);
+                            }
+                        } else {
+                            map.put("type", "image");
+                            map.put("imgUrl", url);
+                            list.add(map);
+                        }
+                    }
+
+                }
+            }
+        } else {
+            LambdaQueryWrapper<StoreImg> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(StoreImg::getStoreId, lifeUserViolation.getId());
+            wrapper.eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_REPORT);
+            List<StoreImg> imgList = storeImgMapper.selectList(wrapper);
+            for (int i = 0; i < imgList.size(); i++) {
+                StoreImg storeImg = imgList.get(i);
+                // 过滤掉imgUrl为空或null的记录
+                if (storeImg == null || StringUtils.isBlank(storeImg.getImgUrl())) {
+                    continue;
+                }
+
+                Map<String, Object> map = new HashMap<>();
+
+                String fileType = storeImg.getImgUrl().substring(storeImg.getImgUrl().lastIndexOf(".") + 1);
+
+                if (videoFileType.contains(fileType.toLowerCase())) {
+                    map.put("type", "video");
+                } else {
+                    map.put("type", "image");
+                }
+                map.put("imgUrl", storeImg.getImgUrl());
+                list.add(map);
+            }
+            secondReportingVo.setVideoFirstFrame(lifeUserViolation.getVideoFirstFrame());
+        }
+
+        secondReportingVo.setImgList(list);
+        secondReportingVo.setReportContextType(lifeUserViolation.getReportContextType());
+        secondReportingVo.setId(id);
+        return secondReportingVo;
+
+    }
+
+    @Override
+    public int reporting(SecondUserViolationVo lifeuserViolation) throws Exception  {
+        try {
+            if ("4".equals(lifeuserViolation.getReportContextType())) {
+                lifeuserViolation.setGoodsId(lifeuserViolation.getBusinessId());
+                LambdaQueryWrapper<SecondGoodsRecord> goodsWrapper = new LambdaQueryWrapper<>();
+                goodsWrapper.eq(SecondGoodsRecord::getGoodsId, lifeuserViolation.getBusinessId());
+                goodsWrapper.eq(SecondGoodsRecord::getGoodsStatus, "3");
+                goodsWrapper.orderByDesc(SecondGoodsRecord::getCreatedTime);
+                goodsWrapper.last(" limit 1 ");
+                SecondGoodsRecord goodsRecord = secondGoodsRecordMapper.selectOne(goodsWrapper);
+                if (null != goodsRecord) lifeuserViolation.setBusinessId(goodsRecord.getId());
+            }
+            int result = lifeUserViolationMapper.insert(lifeuserViolation);
+            if (result > 0) {
+
+                if ("5".equals(lifeuserViolation.getReportContextType())) {
+                    // AI审核
+                    //登录获取token
+                    String token = aiUserViolationUtils.getAccessToken();
+                    //调用AI接口
+                    String taskId = aiUserViolationUtils.createTask(token, lifeuserViolation);
+                    if (org.springframework.util.StringUtils.isEmpty(taskId)) {
+                        log.warn("Failed to create AI task for second round review, lifeuserViolation id={}", lifeuserViolation.getId());
+                        return 0;
+                    }
+                    lifeuserViolation.setAiTaskId(taskId);
+                    lifeUserViolationMapper.updateById(lifeuserViolation);
+                }
+
+                if (lifeuserViolation.getReportContextType().equals("4") || lifeuserViolation.getReportContextType().equals("5")) {
+                    String phoneId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");
+                    // 举报通知
+                    LifeNotice lifeNotice = getLifeNotice(lifeuserViolation);
+                    lifeNoticeMapper.insert(lifeNotice);
+
+                    // 保存图片
+                    saveStoreImages(lifeuserViolation.getId(), lifeuserViolation);
+
+                    WebSocketVo websocketVo = new WebSocketVo();
+                    websocketVo.setSenderId("system");
+                    websocketVo.setReceiverId(phoneId);
+                    websocketVo.setCategory("notice");
+                    websocketVo.setNoticeType("1");
+                    websocketVo.setIsRead(0);
+                    websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeNotice).toJSONString());
+                    alienStoreFeign.sendMsgToClientByPhoneId(lifeNotice.getReceiverId(), com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
+                }
+                return result;
+            }
+        } catch (Exception e) {
+            log.error("SecondGoodsReportingServiceImpl_reporting Error Stack={}", e.getMessage());
+            throw new Exception(e);
+        }
+        return 0;
+    }
+
+    private static LifeNotice getLifeNotice(LifeUserViolation lifeuserViolation)  {
+        JSONObject data = JwtUtil.getCurrentUserInfo();
+        String phoneId = null;
+        if (data != null) {
+            phoneId = data.getString("phone");
+        }
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setSenderId("system");
+        lifeNotice.setReceiverId("user_" + phoneId);
+        lifeNotice.setBusinessId(lifeuserViolation.getId());
+        lifeNotice.setTitle("举报通知");
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("title", "平台已受理");
+        jsonObject.put("message", "平台已受理,感谢您的反馈!");
+        lifeNotice.setContext(jsonObject.toJSONString());
+        lifeNotice.setNoticeType(1);
+        return lifeNotice;
+    }
+
+    /**
+     * 保存商品图片信息
+     * @param id 举报ID
+     * @param goods .getimgUrl 图片URL列表
+     * @return 保存结果
+     */
+    private boolean saveStoreImages(Integer id, SecondUserViolationVo goods ) {
+        if (CollectionUtil.isEmpty(goods.getImgUrl())) {
+            return true; // 如果没有图片,则返回成功
+        }
+        // 批量保存图片信息
+        for(int i = 0; i < goods.getImgUrl().size(); i++){
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(id);
+            storeImg.setImgType(Constants.ImageType.SECOND_HAND_REPORT);
+            storeImg.setImgSort(i);
+            storeImg.setImgDescription("二手举报图片");
+            storeImg.setDeleteFlag(Constants.DeleteFlag.NOT_DELETED);
+            storeImg.setCreatedTime(new Date());
+            storeImg.setUpdatedTime(new Date());
+            storeImg.setCreatedUserId(1);
+            storeImg.setUpdatedUserId(1);
+            storeImg.setImgUrl(goods.getImgUrl().get(i));
+            // 保存图片 插入store_img数据库
+            storeImgMapper.insert(storeImg);
+        }
+        return true;
+    }
+}

+ 2288 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondGoodsServiceImpl.java

@@ -0,0 +1,2288 @@
+package shop.alien.second.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.alipay.api.domain.GoodsVO;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import shop.alien.config.properties.RiskControlProperties;
+import shop.alien.entity.SecondVideoTask;
+import shop.alien.entity.second.*;
+import shop.alien.entity.second.enums.RiskControlRuleTypeEnum;
+import shop.alien.entity.second.enums.SecondGoodsStatusEnum;
+import shop.alien.entity.second.enums.SecondUserCreditScoreEnum;
+import shop.alien.entity.second.vo.*;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.vo.LifeUserVo;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.mapper.*;
+import shop.alien.mapper.second.*;
+import shop.alien.second.feign.AlienStoreFeign;
+import shop.alien.second.service.*;
+import shop.alien.util.common.Constants;
+import shop.alien.util.common.VideoUtils;
+import shop.alien.util.common.safe.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 二手商品服务实现类
+ * 提供二手商品的增删改查、审核、上下架、风控等相关业务逻辑处理
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, SecondGoods> implements SecondGoodsService {
+
+
+    /**
+     * 视频审核功能是否启用的配置项
+     */
+    @Value("${video.moderation.enabled}")
+    private boolean videoModerationEnabled;
+    
+    /**
+     * 视频审核失败时是否阻塞商品发布的配置项
+     */
+    @Value("${video.moderation.block-on-failure}")
+    private boolean videoModerationBlockOnFailure;
+
+    /**
+     * 视频审核服务,用于处理商品中视频内容的审核
+     */
+    private final VideoModerationService videoModerationService;
+
+    /**
+     * 视频工具类,提供视频相关操作功能
+     */
+    private final VideoUtils videoUtils;
+
+    /**
+     * 文本审核工具,用于审核商品标题、描述等文本内容
+     */
+    private final TextModerationUtil textModerationUtil;
+
+    /**
+     * 图片审核工具,用于审核商品图片内容
+     */
+    private final ImageModerationUtil imageModerationUtil;
+
+    /**
+     * 二手商品Mapper,用于操作二手商品数据表
+     */
+    private final SecondGoodsMapper secondGoodsMapper;
+
+    /**
+     * 二手商品审核Mapper,用于操作二手商品审核记录表
+     */
+    private final SecondGoodsAuditMapper secondGoodsAuditMapper;
+
+    /**
+     * 用户信息Mapper,用于操作用户信息表
+     */
+    private final LifeUserMapper lifeUserMapper;
+
+    /**
+     * 公告Mapper,用于操作公告信息表
+     */
+    private final LifeNoticeMapper lifeNoticeMapper;
+
+    /**
+     * 店铺图片Mapper,用于操作图片信息表
+     */
+    private final StoreImgMapper storeImgMapper;
+
+    /**
+     * 黑名单Mapper,用于操作用户黑名单表
+     */
+    private final LifeBlacklistMapper lifeBlacklistMapper;
+
+    /**
+     * 点赞记录Mapper,用于操作点赞记录表
+     */
+    private final LifeLikeRecordMapper lifeLikeRecordMapper;
+
+    /**
+     * 收藏Mapper,用于操作收藏记录表
+     */
+    private final LifeCollectMapper lifeCollectMapper;
+
+    /**
+     * 店铺服务Feign接口,用于跨服务调用
+     */
+    private final AlienStoreFeign alienStoreFeign;
+    
+    /**
+     * 商品操作历史记录Mapper,用于操作商品操作记录表
+     */
+    private final SecondGoodsRecordMapper secondGoodsRecordMapper;
+
+    /**
+     * 二手交易记录Mapper,用于操作二手交易记录表
+     */
+    @Autowired
+    private SecondTradeRecordMapper secondTradeRecordMapper;
+
+    /**
+     * 用户违规举报Mapper,用于操作用户举报记录表
+     */
+    private final LifeUserViolationMapper lifeUserViolationMapper;
+    
+    /**
+     * 字典Mapper,用于操作数据字典表
+     */
+    private final StoreDictionaryMapper storeDictionaryMapper;
+
+    /**
+     * 平台二手交易服务,提供二手交易相关业务逻辑
+     */
+    private final PlatformSecondTradeService platformSecondTradeService;
+
+    /**
+     * 风控配置属性,包含各种风控规则的配置参数
+     */
+    @Autowired
+    private RiskControlProperties riskControlProperties;
+    
+    /**
+     * 风控服务,提供风控相关业务逻辑处理
+     */
+    private final RiskControlService riskControlService;
+
+    private final SecondGoodsAuditService secondGoodsAuditService;
+
+    private final SecondUserCreditMapper secondUserCreditMapper;
+    private final SecondUserCreditRecordMapper secondUserCreditRecordMapper;
+
+    /**
+     * 获取商品操作记录详情(管理后台使用)
+     * @param recordId 商品操作记录ID
+     * @return 商品操作记录详情VO对象
+     */
+    /**
+     * 获取商品操作记录详情(管理后台使用)
+     * @param recordId 商品操作记录ID
+     * @return 商品操作记录详情VO对象
+     */
+    @Override
+    public SecondGoodsRecordDetailVo getAdminGoodsRecordDetail(Integer recordId) {
+        // 1. 获取商品操作记录基本信息
+        QueryWrapper<SecondGoodsRecord> queryWrapper = new QueryWrapper<>();
+        queryWrapper
+                .eq("sg.id", recordId);
+        SecondGoodsRecord record = secondGoodsRecordMapper.selectGoodsRecordById(queryWrapper);
+        if (record == null) {
+            return null;
+        }
+
+        // 2. 转换为VO对象
+        SecondGoodsRecordDetailVo detailVo = SecondGoodsRecordDetailVo.fromRecord(record);
+
+        if (record.getUserId() != null){
+            // 获取联系人
+            QueryWrapper<LifeUser> userQueryWrapper = new QueryWrapper<>();
+            userQueryWrapper.lambda()
+                    .eq(LifeUser::getId, record.getUserId())
+                    .eq(LifeUser::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED);
+            LifeUser user = lifeUserMapper.selectOne(userQueryWrapper);
+
+            detailVo.setUserName(user.getUserName());
+            detailVo.setUserPhone(user.getUserPhone());
+        }
+
+
+        // 3. 获取商品图片列表
+        QueryWrapper<StoreImg> imageQueryWrapper = new QueryWrapper<>();
+        imageQueryWrapper.lambda()
+                .eq(StoreImg::getStoreId, record.getId())
+                .eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_RECORD)
+                .eq(StoreImg::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED)
+                .orderByAsc(StoreImg::getImgSort);
+        List<StoreImg> imageList = storeImgMapper.selectList(imageQueryWrapper);
+        
+        // 4. 提取图片URL列表
+        if (CollectionUtil.isNotEmpty(imageList)) {
+            List<String> imageUrls = imageList.stream()
+                    .map(StoreImg::getImgUrl)
+                    .collect(Collectors.toList());
+            List<Map<String, Object>> imgList = processReportImages(imageList,2);
+            detailVo.setImgList(imgList);
+            detailVo.setImageUrls(imageUrls);
+        }
+
+        return detailVo;
+    }
+
+    /**
+     * 获取商品详情(管理后台使用)
+     * @param goodsId 商品ID
+     * @return 商品详情VO对象
+     * @throws Exception 处理过程中可能抛出的异常
+     */
+    /**
+     * 获取商品详情(管理后台使用)
+     * @param goodsId 商品ID
+     * @return 商品详情VO对象
+     * @throws Exception 处理过程中可能抛出的异常
+     */
+    @Override
+    public SecondGoodsDetailVo getAdminGoodsDetail(Integer goodsId) throws Exception {
+        // 基本信息
+        SecondGoodsDetailVo detailVo =  dealSecondGoodsInfo(goodsId);
+        // 3. 获取商品操作记录集合
+        QueryWrapper<SecondGoodsRecord> recordQueryWrapper = new QueryWrapper<>();
+        recordQueryWrapper.lambda()
+                .eq(SecondGoodsRecord::getGoodsId, goodsId)
+                .orderByDesc(SecondGoodsRecord::getCreatedTime);
+        List<SecondGoodsRecord> operationRecords = secondGoodsRecordMapper.selectdminGoodsList(recordQueryWrapper);
+        detailVo.setOperationRecords(operationRecords);
+        
+        // 4. 获取商品交易记录集合
+        QueryWrapper<SecondTradeRecord> tradeQueryWrapper = new QueryWrapper<>();
+        tradeQueryWrapper.eq("goods_id", goodsId)
+                .orderByDesc("created_time");
+        List<SecondTradeRecord> tradeRecords = secondTradeRecordMapper.selectList(tradeQueryWrapper);
+        // 处理交易步骤 调取 PlatformSecondTradeServiceImpl.getOperationJsonList
+        List<SecondTradeRecordVo> secondTradeRecordVos = Lists.newArrayList();
+        if (CollectionUtil.isNotEmpty(tradeRecords)){
+            for (SecondTradeRecord tradeRecord : tradeRecords) {
+                SecondTradeRecordVo secondTradeRecordVo = new SecondTradeRecordVo();
+                BeanUtils.copyProperties(tradeRecord, secondTradeRecordVo);
+                // 交易节点
+                secondTradeRecordVo.setOperationJsonList(platformSecondTradeService.getOperationJsonList(tradeRecord.getId()));
+                // 获取联系人
+                QueryWrapper<LifeUser> userQueryWrapper = new QueryWrapper<>();
+                userQueryWrapper.lambda()
+                        .eq(LifeUser::getId, secondTradeRecordVo.getBuyerId())
+                        .eq(LifeUser::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED);
+                LifeUser user = lifeUserMapper.selectOne(userQueryWrapper);
+                secondTradeRecordVo.setUserName(user.getUserName());
+                secondTradeRecordVo.setUserPhone(user.getUserPhone());
+                secondTradeRecordVos.add(secondTradeRecordVo);
+            }
+        }
+        detailVo.setTradeRecords(secondTradeRecordVos);
+        
+        // 5. 获取商品举报集合
+        QueryWrapper<LifeUserViolation> reportQueryWrapper = new QueryWrapper<>();
+        reportQueryWrapper.lambda()
+                .eq(LifeUserViolation::getGoodsId, goodsId)
+                .eq(LifeUserViolation::getReportContextType, "4") // 4:二手商品
+                .orderByDesc(LifeUserViolation::getCreatedTime);
+        List<LifeUserViolation> reports = lifeUserViolationMapper.selectList(reportQueryWrapper);
+        
+        // 转换举报信息为SecondReportingVo
+        List<SecondReportingVo> reportingVos = convertReportsToVos(reports);
+        detailVo.setReports(reportingVos);
+        
+        return detailVo;
+    }
+
+    /**
+     * 处理商品信息(管理后台使用)
+     * @param goodsId 商品ID
+     * @return 商品详情VO对象
+     */
+    @Override
+    public SecondGoodsDetailVo dealSecondGoodsInfo(Integer goodsId) {
+        SecondGoodsDetailVo SecondGoodsDetailVo = new SecondGoodsDetailVo();
+        QueryWrapper<SecondGoodsVo> goodsVoQueryWrapper = new QueryWrapper<>();
+        goodsVoQueryWrapper
+                .eq("sg.id", goodsId);
+        // 1. 获取商品基本信息
+        SecondGoodsVo goodsInfo = secondGoodsMapper.getGoodsById(goodsVoQueryWrapper);
+
+        // 2. 获取商品图片列表
+        QueryWrapper<StoreImg> imageQueryWrapper = new QueryWrapper<>();
+        imageQueryWrapper.lambda()
+                .eq(StoreImg::getStoreId, goodsId)
+                .eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_GOODS)
+                .eq(StoreImg::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED)
+                .orderByAsc(StoreImg::getImgSort);
+        List<StoreImg> imageList = storeImgMapper.selectList(imageQueryWrapper);
+        // 提取图片URL列表
+        if (CollectionUtil.isNotEmpty(imageList)) {
+            List<String> imageUrls = imageList.stream()
+                    .map(StoreImg::getImgUrl)
+                    .collect(Collectors.toList());
+            List<Map<String, Object>> imgList = processReportImages(imageList,2);
+            goodsInfo.setImgList(imgList);
+            goodsInfo.setImgUrl(imageUrls);
+        }
+        SecondGoodsDetailVo.setGoodsInfo(goodsInfo);
+        return SecondGoodsDetailVo;
+    }
+
+    /**
+     * 处理商品记录信息
+     * @param goodsId 商品ID
+     * @return 商品VO对象
+     */
+    @Override
+    public SecondGoodsVo dealSecondGoodsRecordInfo(Integer goodsId) {
+        QueryWrapper<SecondGoodsVo> goodsVoQueryWrapper = new QueryWrapper<>();
+        goodsVoQueryWrapper.eq("sg.id", goodsId);
+        // 1. 获取商品基本信息
+        SecondGoodsVo goodsInfo = secondGoodsMapper.getGoodsRecordById(goodsVoQueryWrapper);
+
+        // 2. 获取商品图片列表
+        QueryWrapper<StoreImg> imageQueryWrapper = new QueryWrapper<>();
+        imageQueryWrapper.lambda()
+                .eq(StoreImg::getStoreId, goodsId)
+                .eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_RECORD)
+                .eq(StoreImg::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED)
+                .orderByAsc(StoreImg::getImgSort);
+        List<StoreImg> imageList = storeImgMapper.selectList(imageQueryWrapper);
+        // 提取图片URL列表
+        if (CollectionUtil.isNotEmpty(imageList)) {
+            List<String> imageUrls = imageList.stream()
+                    .map(StoreImg::getImgUrl)
+                    .collect(Collectors.toList());
+            List<Map<String, Object>> imgList = processReportImages(imageList,2);
+            goodsInfo.setImgList(imgList);
+            goodsInfo.setImgUrl(imageUrls);
+        }
+        return goodsInfo;
+    }
+
+    /**
+     * 记录商品操作历史
+     * @param goods 商品信息
+     * @param operationName 操作名称
+     */
+    @Override
+    public void recordGoodsOperation(SecondGoods goods,String operationName) {
+        try {
+            log.warn("开始创建操作历史: {},{} ", goods,operationName);
+            SecondGoodsRecord record = new SecondGoodsRecord();
+            record.setOperationName(operationName);
+            record.setGoodsId(goods.getId());
+            record.setUserId(goods.getUserId());
+            record.setTitle(goods.getTitle());
+            record.setDescription(goods.getDescription());
+            // 价格转换
+            if (goods.getAmount() != null) {
+                record.setPrice(goods.getAmount());
+            } else if (goods.getPrice() != null && !goods.getPrice().isEmpty()) {
+                try {
+                    record.setPrice(new BigDecimal(goods.getPrice()));
+                } catch (Exception e) {
+                    log.warn("转换商品价格时出错: {}", e.getMessage());
+                }
+            }
+            record.setPosition(goods.getPosition());
+            record.setLikeCount(goods.getLikeCount());
+            record.setCollectCount(goods.getCollectCount());
+            record.setCategoryOneId(goods.getCategoryOneId());
+            record.setCategoryTwoId(goods.getCategoryTwoId());
+            record.setLabel(goods.getLabel());
+            record.setTopic(goods.getTopic());
+            record.setTradeId(goods.getTradeId());
+            record.setReleaseTime(goods.getReleaseTime());
+            record.setGoodsStatus(goods.getGoodsStatus());
+            record.setFailedReason(goods.getFailedReason());
+            record.setHomeImage(goods.getHomeImage());
+            record.setVideoTaskId(goods.getVideoTaskId());
+            record.setVideoFirstFrame(goods.getVideoFirstFrame());
+            record.setDeleteFlag(goods.getDeleteFlag());
+            record.setCreatedTime(goods.getCreatedTime());
+            record.setCreatedUserId(goods.getCreatedUserId());
+            record.setUpdatedTime(goods.getUpdatedTime());
+            record.setUpdatedUserId(goods.getUpdatedUserId());
+            record.setAddressText(goods.getAddressText());
+            
+            secondGoodsRecordMapper.insert(record);
+            log.warn("创建操作历史结束: {} ", record);
+            // 保存图片信息
+            saveRecordGoodsImages(record);
+        } catch (Exception e) {
+            log.error("记录商品操作历史时发生异常", e);
+        }
+    }
+
+
+    /**
+     * 保存二手商品记录图片类型信息
+     *
+     * @param record 保存后的商品记录
+     */
+    /**
+     * 保存二手商品记录图片类型信息
+     *
+     * @param record 保存后的商品记录
+     */
+    private void saveRecordGoodsImages(SecondGoodsRecord record ) {
+
+        log.info("创建操作历图片史开始: {} ", record);
+        // 获取商品图片列表
+        QueryWrapper<StoreImg> query = new QueryWrapper<>();
+        query.lambda().eq(StoreImg::getStoreId, record.getGoodsId())
+                .eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_GOODS);
+        List<StoreImg> storeImgs = storeImgMapper.selectList(query);
+        // 保存前先把原有的删除
+        storeImgs.forEach(storeImgModel -> {
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(record.getId());
+            storeImg.setImgType(Constants.ImageType.SECOND_HAND_RECORD);
+            storeImg.setImgSort(storeImgModel.getImgSort());
+            storeImg.setImgDescription("二手商品记录图片类型");
+            storeImg.setDeleteFlag(Constants.DeleteFlag.NOT_DELETED);
+            storeImg.setCreatedTime(new Date());
+            storeImg.setUpdatedTime(new Date());
+            storeImg.setCreatedUserId(1);
+            storeImg.setUpdatedUserId(1);
+            storeImg.setImgUrl(storeImgModel.getImgUrl());
+            // 保存图片 插入store_img数据库
+            storeImgMapper.insert(storeImg);
+            log.info("创建操作历图片结束: {} ", storeImg);
+        });
+    }
+
+    /**
+     * 批量转换举报信息为SecondReportingVo对象
+     * @param reports 举报信息列表
+     * @return SecondReportingVo列表
+     */
+    /**
+     * 批量转换举报信息为SecondReportingVo对象
+     * @param reports 举报信息列表
+     * @return SecondReportingVo列表
+     */
+    private List<SecondReportingVo> convertReportsToVos(List<LifeUserViolation> reports) {
+        List<SecondReportingVo> reportingVos = new ArrayList<>();
+        
+        // 获取所有相关的举报类型字典信息
+        List<String> dictTypes = reports.stream()
+                .map(LifeUserViolation::getDictType)
+                .distinct()
+                .collect(Collectors.toList());
+        
+        List<Integer> dictIds = reports.stream()
+                .map(LifeUserViolation::getDictId)
+                .distinct()
+                .collect(Collectors.toList());
+        
+        Map<String, StoreDictionary> dictMap = new HashMap<>();
+        if (!dictTypes.isEmpty() && !dictIds.isEmpty()) {
+            QueryWrapper<StoreDictionary> dictQueryWrapper = new QueryWrapper<>();
+            dictQueryWrapper.lambda()
+                    .in(StoreDictionary::getTypeName, dictTypes)
+                    .in(StoreDictionary::getDictId, dictIds);
+            List<StoreDictionary> dicts = storeDictionaryMapper.selectList(dictQueryWrapper);
+            dictMap = dicts.stream()
+                    .collect(Collectors.toMap(
+                        d -> d.getTypeName() + "_" + d.getDictId(), 
+                        d -> d));
+        }
+        
+        // 转换每个举报信息
+        for (LifeUserViolation report : reports) {
+            SecondReportingVo reportingVo = new SecondReportingVo();
+            BeanUtils.copyProperties(report, reportingVo);
+
+            // 查询用户表 根据举报用户类型和举报用户ID 查询 life_user 表 user_phone
+            LifeUser reporter = lifeUserMapper.selectById(report.getReportingUserId());
+            if (reporter != null) {
+                // 处理举报用户名称  life_user 表 user_name
+                reportingVo.setReportingUserName(reporter.getUserName());
+                // 处理联系方式  life_user 表 user_phone
+                reportingVo.setReportingUserPhone(reporter.getUserPhone());
+            }
+
+            // 基本信息
+            reportingVo.setId(report.getId());
+            // 处理状态
+            reportingVo.setProcessingStatus(report.getProcessingStatus());
+            
+            // 举报时间
+            reportingVo.setReportingTime(report.getCreatedTime());
+            // 举报内容补充
+            reportingVo.setReportingContext(report.getOtherReasonContent());
+//            reportingVo.setFeedbackTime(report.getCreatedTime());
+//            reportingVo.setFeedbackContext("平台已受理,感谢您的反馈!");
+            
+            // 获取举报类型信息
+            StoreDictionary storeDictionary = dictMap.get(report.getDictType() + "_" + report.getDictId());
+            // 举报类型
+            reportingVo.setReportContextType(storeDictionary.getDictDetail());
+            // 二手商品举报
+            SecondGoods secondGoods = secondGoodsMapper.selectById(report.getBusinessId());
+            if (secondGoods != null) {
+                reportingVo.setPrice(secondGoods.getPrice() != null ?
+                        secondGoods.getPrice().toString() : null);
+                reportingVo.setHomeImage(secondGoods.getHomeImage());
+                reportingVo.setTitle(secondGoods.getTitle());
+                reportingVo.setDescription(secondGoods.getDescription());
+            }
+            // 处理举报凭证图片
+            // 查询图片信息
+            LambdaQueryWrapper<StoreImg> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(StoreImg::getStoreId, report.getId());
+            wrapper.eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_REPORT);
+            List<StoreImg> imgList = storeImgMapper.selectList(wrapper);
+            if (CollectionUtil.isNotEmpty(imgList)) {
+                // 提取图片URL
+                List<String> urlList = imgList.stream()
+                        .map(StoreImg::getImgUrl)
+                        .collect(Collectors.toList());
+                reportingVo.setImgList(processReportImages(imgList,1));
+            }
+            
+            reportingVos.add(reportingVo);
+        }
+        
+        return reportingVos;
+    }
+
+    /**
+     * 处理举报凭证图片
+     * @param imageList 图片URL集合
+     * @param type  类型 1-举报 2-商品
+     * @return 图片列表
+     */
+    /**
+     * 处理举报凭证图片
+     * @param imageList 图片URL集合
+     * @param type  类型 1-举报 2-商品
+     * @return 图片列表
+     */
+    private List<Map<String, Object>> processReportImages(List<StoreImg> imageList, Integer type) {
+        List<Map<String, Object>> list = new ArrayList<>();
+        List<String> videoFileType = Arrays.asList("mp4", "avi", "flv", "mkv", "rmvb", "wmv", "3gp", "mov");
+
+        for (StoreImg img : imageList) {
+            // 过滤掉imgUrl为空或null的记录
+            if (img == null || StringUtils.isBlank(img.getImgUrl())) {
+                continue;
+            }
+            
+            Map<String, Object> map = new HashMap<>();
+            String fileType = img.getImgUrl().substring(img.getImgUrl().lastIndexOf(".") + 1);
+            if (videoFileType.contains(fileType.toLowerCase())) {
+                map.put("type", "video");
+            } else {
+                map.put("type", "image");
+            }
+            map.put("imgUrl", img.getImgUrl());
+            if (type == 1) {
+                map.put("videoUrl", img.getImgUrl());
+            }
+            list.add(map);
+        }
+
+        return list;
+    }
+
+    /**
+     * 保存商品为草稿状态
+     * @param goods 商品实体
+     * @return 是否成功保存
+     */
+    @Override
+    public boolean saveAsDraft(SecondGoodsVo goods) {
+        // 设置商品状态为草稿
+        goods.setGoodsStatus(SecondGoodsStatusEnum.DRAFT.getCode());
+        if (null != goods.getId()){
+            // 更新商品基本信息
+            if (!updateById(goods)) {
+                return false; // 保存失败直接返回
+            }
+        }else {
+            // 保存商品基本信息
+            if (!save(goods)) {
+                return false; // 保存失败直接返回
+            }
+        }
+        // 获取保存后的商品ID,用于后续业务处理
+        Integer savedGoodsId = goods.getId();
+        if (savedGoodsId == null) {
+            return false; // 如果获取不到ID,视为操作失败
+        }
+        // 保存商品图片信息
+        return saveStoreImages(savedGoodsId, goods);
+    }
+
+
+    /**
+     * 创建商品基本信息
+     * @param goodsDTO 商品信息DTO
+     * @param editFlag 编辑标识 1-编辑 0-新增
+     * @return 是否创建成功
+     * @throws Exception 处理过程中可能抛出的异常
+     */
+    @Override
+    public boolean createBasicInfo(SecondGoodsVo goodsDTO,Integer editFlag) throws Exception {
+        try {
+            // 实现基本信息保存逻辑
+            SecondGoods goods = new SecondGoods();
+            BeanUtils.copyProperties(goodsDTO, goods);
+
+            boolean saveResult;
+            if (editFlag == 1) {
+                goods.setId(goodsDTO.getId());
+//                goods = secondGoodsMapper.selectById(goodsDTO.getId());
+                // 保存商品基本信息
+                saveResult = updateById(goods);
+            } else {
+                // 保存商品基本信息
+                saveResult = save(goods);
+            }
+            
+            if (!saveResult) {
+                return false; // 保存失败直接返回
+            }
+
+            // 获取保存后的商品ID,用于后续业务处理
+            Integer savedGoodsId = goods.getId();
+            if (savedGoodsId == null) {
+                return false; // 如果获取不到ID,视为操作失败
+            }
+
+            // 保存商品图片信息
+            if (!saveStoreImages(savedGoodsId, goodsDTO )) {
+                return false;
+            }
+            goods = secondGoodsMapper.selectById(savedGoodsId);
+            // 审核不通过时已设置状态,返回成功但标记为审核失败
+            performContentReview(goodsDTO, goods);
+
+            return true;
+        } catch (Exception e) {
+            // 记录异常日志
+            log.error("创建或更新二手商品基本信息时发生异常", e);
+            return false;
+        }
+    }
+
+
+    /**
+     * 检查用户在24小时内 频繁修改发布商品的数量是否超过限制
+     * @param goods  用户ID,商品ID
+     * @return 是否未超过限制
+     */
+    /**
+     * 检查用户在24小时内 频繁修改发布商品的数量是否超过限制
+     * @param goods  用户ID,商品ID
+     * @return 是否未超过限制
+     */
+    private boolean checkUserPublishLimit(SecondGoods goods) {
+        // 获取配置的阈值
+        int publishLimit = riskControlProperties.getTradeFraud().getPublishCount24h();
+        // 时间窗口(小时)
+        int timeWindowHours = riskControlProperties.getTradeFraud().getTimeWindowHours();
+
+        // 计算时间窗口前的时间
+        Date timeWindowStart = new Date(System.currentTimeMillis() - timeWindowHours * 60 * 60 * 1000L);
+
+        // 查询用户在24小时内发布的商品数量
+        LambdaQueryWrapper<SecondGoodsRecord> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SecondGoodsRecord::getUserId, goods.getUserId())
+                .eq(SecondGoodsRecord::getGoodsId, goods.getId())
+                .ge(SecondGoodsRecord::getReleaseTime, timeWindowStart)
+                .in(SecondGoodsRecord::getGoodsStatus,
+                        SecondGoodsStatusEnum.LISTED.getCode(),
+                        SecondGoodsStatusEnum.SOLD.getCode());
+        // 获取发布
+        List<SecondGoodsRecord> secondGoodsRecordList = secondGoodsRecordMapper.selectList(queryWrapper);
+        // 提取商品id集合
+        List<Integer> secondGoodsRecordIdList = secondGoodsRecordList.stream().map(SecondGoodsRecord::getId).collect(Collectors.toList());
+        // 转成json
+        String json = JSON.toJSONString(secondGoodsRecordIdList);
+        // 获取实际发布数量
+        int sameCategoryCount = secondGoodsRecordList.size();
+        // 如果发布数量超过限制,记录风控数据
+        if (sameCategoryCount > publishLimit) {
+            // "异常发布-同类商品发布频率"
+//            riskControlService.recordRiskControlData(goods.getUserId(), RiskControlRuleTypeEnum.TRANSACTION_FRAUD.getRuleType(), RiskControlRuleTypeEnum.TRANSACTION_FRAUD.getDescription(), goods.getId().toString(),json);
+
+            String startDate = LocalDateTime.now().minusHours(24L).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+            String endDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+            List<SecondUserCreditRecord> num = secondUserCreditRecordMapper.selectList(new LambdaQueryWrapper<SecondUserCreditRecord>()
+                    .eq(SecondUserCreditRecord::getUserId, goods.getUserId())
+                    .eq(SecondUserCreditRecord::getPointsType, SecondUserCreditScoreEnum.TRADING_FRAUD.getCode())
+                    .ge(SecondUserCreditRecord::getCreatedTime, startDate)
+                    .le(SecondUserCreditRecord::getCreatedTime, endDate));
+            if (num.size() <= 0) {
+                LambdaQueryWrapper<SecondUserCredit> queryWrapper1 = new LambdaQueryWrapper<>();
+                queryWrapper1.eq(SecondUserCredit::getUserId, goods.getUserId())
+                        .orderByDesc(SecondUserCredit::getCreatedTime).last("LIMIT 1");
+                SecondUserCredit secondUserCredit = secondUserCreditMapper.selectOne(queryWrapper1);
+                int score = secondUserCredit.getUserPoints() + SecondUserCreditScoreEnum.TRADING_FRAUD.getScore();
+                secondUserCredit.setUserPoints(score);
+                secondUserCreditMapper.updateById(secondUserCredit);
+
+                SecondUserCreditRecord record = new SecondUserCreditRecord();
+                record.setUserId(goods.getUserId());
+                record.setPointsType(SecondUserCreditScoreEnum.TRADING_FRAUD.getCode());
+                record.setPoints(SecondUserCreditScoreEnum.TRADING_FRAUD.getScore());
+                record.setCurrentScoreCount(score);
+                record.setCreatedTime(new Date());
+                secondUserCreditRecordMapper.insert(record);
+
+                // 小于100分封禁用户
+                if (score < 100) {
+                    LifeUser user = new LifeUser();
+                    user.setId(goods.getUserId());
+                    user.setIsBanned(1);
+                    lifeUserMapper.updateById(user);
+                }
+            }
+
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 检查用户在指定时间窗口内发布同类商品的数量是否超过限制
+     * @param goods 商品信息
+     * @return 是否未超过限制
+     */
+    /**
+     * 检查用户在指定时间窗口内发布同类商品的数量是否超过限制
+     * @param goods 商品信息
+     * @return 是否未超过限制
+     */
+    private boolean checkUserPublishSameCategoryLimit(SecondGoods goods) {
+        // 获取配置的阈值
+        int sameCategoryLimit = riskControlProperties.getAbnormalPublish().getSameCategoryCount24h();
+        int timeWindowHours = riskControlProperties.getAbnormalPublish().getTimeWindowHours();
+        
+        // 计算时间窗口前的时间
+        Date timeWindowStart = new Date(System.currentTimeMillis() - timeWindowHours * 60 * 60 * 1000L);
+        
+        // 查询用户在时间窗口内发布同类商品的数量
+        LambdaQueryWrapper<SecondGoods> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SecondGoods::getUserId, goods.getUserId())
+                .eq(SecondGoods::getCategoryOneId, goods.getCategoryOneId())
+                .eq(SecondGoods::getCategoryTwoId, goods.getCategoryTwoId())
+                .ge(SecondGoods::getReleaseTime, timeWindowStart)
+                .in(SecondGoods::getGoodsStatus,
+                        SecondGoodsStatusEnum.LISTED.getCode(),
+                        SecondGoodsStatusEnum.SOLD.getCode());
+
+        // 获取发布
+        List<SecondGoods> secondGoodsList = list(queryWrapper);
+        // 提取商品id集合
+        List<Integer> secondGoodsIds = secondGoodsList.stream().map(SecondGoods::getId).collect(Collectors.toList());
+        // 转成json
+        String json = JSON.toJSONString(secondGoodsIds);
+        // 获取实际发布数量
+        int sameCategoryCount = secondGoodsList.size();
+        // 如果发布数量超过限制,记录风控数据
+        if (sameCategoryCount > sameCategoryLimit) {
+            // "异常发布-同类商品发布频率"
+//            riskControlService.recordRiskControlData(goods.getUserId(), RiskControlRuleTypeEnum.ABNORMAL_PUBLISH.getRuleType(), RiskControlRuleTypeEnum.ABNORMAL_PUBLISH.getDescription(), goods.getId().toString(),json);
+
+            String startDate = LocalDateTime.now().minusHours(24L).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+            String endDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+            List<SecondUserCreditRecord> num = secondUserCreditRecordMapper.selectList(new LambdaQueryWrapper<SecondUserCreditRecord>()
+                    .eq(SecondUserCreditRecord::getUserId, goods.getUserId())
+                    .eq(SecondUserCreditRecord::getPointsType, SecondUserCreditScoreEnum.ABNORMAL_RELEASE.getCode())
+                    .ge(SecondUserCreditRecord::getCreatedTime, startDate)
+                    .le(SecondUserCreditRecord::getCreatedTime, endDate));
+            if (num.size() <= 0) {
+                LambdaQueryWrapper<SecondUserCredit> queryWrapper1 = new LambdaQueryWrapper<>();
+                queryWrapper1.eq(SecondUserCredit::getUserId, goods.getUserId())
+                        .orderByDesc(SecondUserCredit::getCreatedTime).last("LIMIT 1");
+                SecondUserCredit secondUserCredit = secondUserCreditMapper.selectOne(queryWrapper1);
+                int score = secondUserCredit.getUserPoints() + SecondUserCreditScoreEnum.ABNORMAL_RELEASE.getScore();
+                secondUserCredit.setUserPoints(score);
+                secondUserCreditMapper.updateById(secondUserCredit);
+
+                SecondUserCreditRecord record = new SecondUserCreditRecord();
+                record.setUserId(goods.getUserId());
+                record.setPointsType(SecondUserCreditScoreEnum.ABNORMAL_RELEASE.getCode());
+                record.setPoints(SecondUserCreditScoreEnum.ABNORMAL_RELEASE.getScore());
+                record.setCurrentScoreCount(score);
+                record.setCreatedTime(new Date());
+                secondUserCreditRecordMapper.insert(record);
+
+                // 小于100分封禁用户
+                if (score < 100) {
+                    LifeUser user = new LifeUser();
+                    user.setId(goods.getUserId());
+                    user.setIsBanned(1);
+                    lifeUserMapper.updateById(user);
+                }
+            }
+
+
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 执行商品发布风控检查
+     * @param goods 商品信息
+     */
+    @Override
+    public void performPublishRiskCheck(SecondGoods goods) {
+        // 检查用户是否在24小时内发布同类商品超过阈值
+        if (!checkUserPublishSameCategoryLimit(goods)) {
+            log.warn("用户 {} 在24小时内发布同类商品次数超过限制", goods.getUserId());
+        }
+
+        // 检查用户是否在24小时内发布商品超过阈值
+        if (!checkUserPublishLimit(goods)) {
+            log.warn("用户 {} 在24小时内发布商品次数超过限制", goods.getUserId());
+        }
+    }
+
+    /**
+     * 执行内容审核
+     * @param goodsDTO 商品信息
+     * @param goods 商品实体
+     */
+    private void performContentReview(SecondGoodsVo goodsDTO, SecondGoods goods) throws Exception {
+        // 图片审核
+        boolean imageAuditResult = performImageReviews(goods, goodsDTO);
+        /* 添加截断,避免审核结果过多,三个顺序执行,第一个失败后直接返回,以此类推 */
+
+        // 审核失败。直接返回
+        if (!imageAuditResult) {
+            // 图片审核不通过,记录操作历史
+            recordGoodsOperation(goods,"图片审核失败");
+            return;
+        }
+
+        // 文本审核
+        boolean textAuditResult = performTextReview(goods, goodsDTO);
+        // 审核失败。直接返回
+        if (!textAuditResult) {
+            // 文本审核不通过,记录操作历史
+            recordGoodsOperation(goods,"文本审核失败");
+            return;
+        }
+
+        // 视频审核
+        List<String> taskIds = performVideoReviews(goods, goodsDTO);
+
+        // 如果成功提交了视频审核任务,设置商品状态为审核中
+        if (!taskIds.isEmpty()) {
+            goods.setGoodsStatus(SecondGoodsStatusEnum.UNDER_REVIEW.getCode()); // 审核中
+            goods.setVideoTaskId(taskIds.get(0)); // 保存第一个任务ID到商品表
+            goods.setFailedReason("");
+            updateById(goods);
+            // 审核中审核记录
+            createGoodsAudit(goods, "", Constants.AuditStatus.UNDER_REVIEW);
+            // 审核中,记录操作历史
+//            recordGoodsOperation(goods);
+            return;
+        }
+
+        List<String> videoUrls = extractVideoUrls(goodsDTO.getImgUrl());
+        if (videoUrls.isEmpty()) {
+            // ai 审核
+            secondGoodsAuditService.performSecondRoundReview(goods, goodsDTO);
+            return;
+        }
+
+        // 审核通过后上架商品
+        approveAndListGoods(goods);
+
+        // 检查用户是否在24小时内发布同类商品超过阈值
+        if (!checkUserPublishSameCategoryLimit(goods)) {
+            log.warn("用户 {} 在24小时内发布同类商品次数超过限制", goodsDTO.getUserId());
+        }
+
+        // 检查用户是否在24小时内发布商品超过阈值
+        if (!checkUserPublishLimit(goods)) {
+            log.warn("用户 {} 在24小时内发布商品次数超过限制", goodsDTO.getUserId());
+
+        }
+    }
+
+    /**
+     * 创建商品审核记录
+     * @param goods 商品信息
+     */
+//    @Transactional(rollbackFor = Exception.class)
+    private void approveAndListGoods(SecondGoods goods) {
+        boolean isNotified = false;
+        try {
+            // 如果所有审核都通过,设置为上架状态
+            goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode()); // 上架
+            goods.setFailedReason("");
+            goods.setReleaseTime(new Date()); // 上架时间
+            updateById(goods);
+            // 插入审核记录
+            createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+            // 发送审核成功消息
+            sendMessage(goods);
+            isNotified = true; // 标记通知已发送
+            // 上架 记录商品操作历史
+            String operationName = "";
+            QueryWrapper<SecondGoodsRecord> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("goods_id", goods.getId());
+            List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryWrapper);
+            if (CollectionUtil.isNotEmpty(recordList)){
+                operationName = "重新发布";
+            }else {
+                operationName = "首次发布";
+            }
+            recordGoodsOperation(goods, operationName);
+        } catch (Exception e) {
+            log.error("商品上架过程中发生异常,执行回滚", e);
+            // 如果通知已发送但后续操作失败,需要补偿
+            if (isNotified) {
+                // 发送补偿消息,比如撤销通知或标记为异常状态
+//                sendCompensationMessage(goods);
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * 执行视频审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    private List<String> performVideoReviews(SecondGoods goods, SecondGoodsVo goodsDTO) {
+        List<String> videoUrls = extractVideoUrls(goodsDTO.getImgUrl());
+        List<String> taskIds = new ArrayList<>();
+        // 视频审核
+        if (videoModerationEnabled) {
+            if (!videoUrls.isEmpty()) {
+                // 提交视频审核任务
+                for (String videoUrl : videoUrls) {
+                    try {
+                        String taskId = videoModerationService.submitVideoModerationTask(videoUrl);
+                        taskIds.add(taskId);
+                    } catch (Exception e) {
+                        log.error("提交视频审核任务失败,视频URL: {}", videoUrl, e);
+                        if (videoModerationBlockOnFailure) {
+                            // 视频审核提交失败,设置为审核失败状态
+                            goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+                            goods.setFailedReason("视频审核提交失败: " + e.getMessage());
+                            createGoodsAudit(goods, "视频审核提交失败", Constants.AuditStatus.FAILED);
+                            sendFailedMsg(goods);
+                        }
+                    }
+                }
+            }
+        }
+        return taskIds;
+    }
+
+    /**
+     * 执行文本审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    private boolean performTextReview(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception {
+        List<String> servicesList = Lists.newArrayList();
+        servicesList.add(TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService());
+        servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
+        // 使用商品发布场景的审核服务
+        String test = goodsDTO.getDescription() + goodsDTO.getTitle() + goods.getLabel() + goods.getTopic();
+        TextModerationResultVO textCheckResult = textModerationUtil.invokeFunction(test, servicesList);
+
+        if ("high".equals(textCheckResult.getRiskLevel())) {
+            // 文本审核不通过或存在高风险
+            goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode()); // 审核失败
+            String failReason = "文本审核不通过:" + (textCheckResult.getRiskWords() != null ? textCheckResult.getRiskWords() : "存在高风险内容");
+            goods.setFailedReason(failReason);
+            // 插入审核记录
+            createGoodsAudit(goods, failReason, Constants.AuditStatus.FAILED);
+            // 发送审核失败消息
+            sendFailedMsg(goods);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 执行图片审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    private boolean performImageReviews(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception {
+        // 图片审核(循环处理)
+        List<String> imageUrls = goodsDTO.getImgUrl();
+        //  根据imageUrls 过滤不是图片的url
+        imageUrls = imageUrls.stream().filter(url -> url.toLowerCase().endsWith(".jpg") || url.toLowerCase().endsWith(".png") || url.toLowerCase().endsWith(".jpeg") || url.toLowerCase().endsWith(".gif")).collect(Collectors.toList());
+        // 图片审核
+        if (imageUrls != null && !imageUrls.isEmpty()) {
+            StringBuilder failReasonBuilder = new StringBuilder();
+            for (String imageUrl : imageUrls) {
+                List<String> imgServicesList = Lists.newArrayList();
+                // 内容治理检测 + AIGC图片风险检测
+                imgServicesList.add(ImageReviewServiceEnum.TONALITY_IMPROVE.getService());
+                imgServicesList.add(ImageReviewServiceEnum.AIGC_CHECK.getService());
+//                imgServicesList.add(ImageReviewServiceEnum.IMG_QUERY_SECURITY_CHECK.getService());
+                ImageModerationResultVO response = imageModerationUtil.productPublishCheck(imageUrl,imgServicesList);
+                if ("high".equals(response.getRiskLevel())) {
+                    // 图片审核不通过或存在高风险
+                    goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode()); // 审核失败
+                    String failReason = "图片审核不通过:图片中包含" + (response.getDescriptions() != null ? response.getDescriptions() : "高风险内容");
+                    goods.setFailedReason(failReason);
+                    // 插入审核记录
+                    createGoodsAudit(goods, failReason, Constants.AuditStatus.FAILED);
+                    // 发送审核失败消息
+                    sendFailedMsg(goods);
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+
+
+    /**
+     * 执行内容审核(图片、文本和视频)
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    private boolean performContentReviews(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception {
+        List<String> servicesList = Lists.newArrayList();
+        servicesList.add(TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService());
+        servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
+        // 使用商品发布场景的审核服务
+        String test = goodsDTO.getDescription() + goodsDTO.getTitle() + goods.getLabel() + goods.getTopic();
+        TextModerationResultVO textCheckResult = textModerationUtil.invokeFunction(test, servicesList);
+        
+        if ("high".equals(textCheckResult.getRiskLevel())) {
+            // 文本审核不通过或存在高风险
+            goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode()); // 审核失败
+            goods.setFailedReason("文本审核不通过:" + (textCheckResult.getRiskWords() != null ? textCheckResult.getRiskWords() : "存在高风险内容"));
+            // 插入审核记录
+            createGoodsAudit(goods, textCheckResult.getRiskWords(), Constants.AuditStatus.FAILED);
+            // 发送审核失败消息
+            sendFailedMsg(goods);
+            return false;
+        }
+
+        // 图片审核(循环处理)
+        List<String> imageUrls = goodsDTO.getImgUrl();
+        if (!StringUtils.isEmpty(goodsDTO.getHomeImage())){
+            imageUrls.add(goodsDTO.getHomeImage());
+        }
+        if (imageUrls != null && !imageUrls.isEmpty()) {
+            StringBuilder failReasonBuilder = new StringBuilder();
+            for (String imageUrl : imageUrls) {
+                List<String> imgServicesList = Lists.newArrayList();
+                // TODO 后续配置到数据库 中
+                imgServicesList.add(ImageReviewServiceEnum.TONALITY_IMPROVE.getService());
+                imgServicesList.add(ImageReviewServiceEnum.AIGC_CHECK.getService());
+//                imgServicesList.add(ImageReviewServiceEnum.IMG_QUERY_SECURITY_CHECK.getService());
+                ImageModerationResultVO response = imageModerationUtil.productPublishCheck(imageUrl,imgServicesList);
+                if ("high".equals(response.getRiskLevel())) {
+                    // 图片审核不通过或存在高风险
+                    goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode()); // 审核失败
+                    goods.setFailedReason("图片审核不通过:图片中包含" + (response.getDescriptions() != null ? response.getDescriptions() : "高风险内容"));
+                    // 插入审核记录
+                    createGoodsAudit(goods, response.getDescriptions(), Constants.AuditStatus.FAILED);
+                    // 发送审核失败消息
+                    sendFailedMsg(goods);
+                    return false;
+                }
+            }
+        }
+        
+        // 视频审核
+        if (videoModerationEnabled) {
+            List<String> videoUrls = extractVideoUrls(goodsDTO.getImgUrl());
+            if (!videoUrls.isEmpty()) {
+                // 提交视频审核任务
+                List<String> taskIds = new ArrayList<>();
+                for (String videoUrl : videoUrls) {
+                    try {
+                        String taskId = videoModerationService.submitVideoModerationTask(videoUrl);
+                        taskIds.add(taskId);
+                    } catch (Exception e) {
+                        log.error("提交视频审核任务失败,视频URL: {}", videoUrl, e);
+                        if (videoModerationBlockOnFailure) {
+                            // 视频审核提交失败,设置为审核失败状态
+                            goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+                            goods.setFailedReason("视频审核提交失败: " + e.getMessage());
+                            createGoodsAudit(goods, "视频审核提交失败", Constants.AuditStatus.FAILED);
+                            sendFailedMsg(goods);
+                            return false;
+                        }
+                    }
+                }
+                
+                // 如果成功提交了视频审核任务,设置商品状态为审核中
+                if (!taskIds.isEmpty()) {
+                    goods.setGoodsStatus(SecondGoodsStatusEnum.UNDER_REVIEW.getCode()); // 审核中
+                    goods.setVideoTaskId(taskIds.get(0)); // 保存第一个任务ID到商品表
+                    goods.setFailedReason("");
+                    updateById(goods);
+                    createGoodsAudit(goods, "", SecondGoodsStatusEnum.UNDER_REVIEW.getCode());
+                    // 审核中,记录操作历史
+//                    recordGoodsOperation(goods);
+                    return true; // 异步处理,直接返回
+                }
+            }
+        }
+
+        // 如果所有审核都通过,设置为上架状态
+        goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode()); // 上架
+        goods.setFailedReason("");
+        goods.setReleaseTime(new Date()); // 上架时间
+        updateById(goods);
+        // 插入审核记录
+        createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+        // 发送审核成功消息
+        sendMessage(goods);
+        // 审核成功,记录操作历史
+        String operationName = "";
+        QueryWrapper<SecondGoodsRecord> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("goods_id", goods.getId());
+        List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryWrapper);
+        if (CollectionUtil.isNotEmpty(recordList)){
+            operationName = "重新发布";
+        }else {
+            operationName = "首次发布";
+        }
+        recordGoodsOperation(goods, operationName);
+        return true;
+    }
+    
+    /**
+     * 从图片URL列表中提取视频URL
+     * @param imageUrls 图片URL列表
+     * @return 视频URL列表
+     */
+    private List<String> extractVideoUrls(List<String> imageUrls) {
+        if (CollectionUtil.isEmpty(imageUrls)) {
+            return Collections.emptyList();
+        }
+        
+        List<String> videoUrls = new ArrayList<>();
+        for (String url : imageUrls) {
+            if (isVideoUrl(url)) {
+                videoUrls.add(url);
+            }
+        }
+        return videoUrls;
+    }
+
+    /**
+     * 创建商品审核记录
+     *
+     * @param goods 商品
+     * @param failReason 审核结果
+     */
+    private void createGoodsAudit(SecondGoods goods, String failReason,Integer goodsStatus) {
+        // 保存审核结果
+        secondGoodsMapper.updateById(goods);
+        // 插入审核记录
+        SecondGoodsAudit auditRecord = new SecondGoodsAudit();
+        auditRecord.setGoodsId(goods.getId());
+        auditRecord.setGoodsStatus(goodsStatus); // 审核状态
+        if (Constants.AuditStatus.FAILED.equals(goodsStatus)) {
+            auditRecord.setFailedReason(failReason);
+        }
+        auditRecord.setCreatedUserId(goods.getUserId());
+        auditRecord.setUpdatedUserId(goods.getUserId());
+        auditRecord.setCreatedTime(new Date());
+        auditRecord.setUpdatedTime(new Date());
+        secondGoodsAuditMapper.insert(auditRecord);
+        goods.setAuditRecordId(auditRecord.getId());
+    }
+
+    /**
+     * 保存商品图片信息
+     * @param savedGoodsId 保存后的商品ID
+     * @param goods .getimgUrl 图片URL列表
+     * @return 保存结果
+     */
+    private boolean saveStoreImages(Integer savedGoodsId, SecondGoodsVo goods ) {
+        if (CollectionUtil.isEmpty(goods.getImgUrl())) {
+            return true; // 如果没有图片,则返回成功
+        }
+        // 处理 第一个值为视频地址,获取视频第一帧作为商品封面图
+        String coverImage = getCoverImageFromVideoOrImage(goods);
+        if (coverImage != null) {
+            // 更新商品表封面图字段
+            SecondGoods secondGoods = new SecondGoods();
+            secondGoods.setId(savedGoodsId);
+            secondGoods.setHomeImage(coverImage);
+            updateById(secondGoods);
+        }
+        // 保存前先把原有的删除
+        storeImgMapper.delete(new LambdaUpdateWrapper<StoreImg>().eq(StoreImg::getStoreId, savedGoodsId).eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_GOODS));
+        // 批量保存图片信息
+        for(int i = 0; i < goods.getImgUrl().size(); i++){
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(savedGoodsId);
+            storeImg.setImgType(Constants.ImageType.SECOND_HAND_GOODS);
+            storeImg.setImgSort(i);
+            storeImg.setImgDescription("发布二手商品图片");
+            storeImg.setDeleteFlag(Constants.DeleteFlag.NOT_DELETED);
+            storeImg.setCreatedTime(new Date());
+            storeImg.setUpdatedTime(new Date());
+            storeImg.setCreatedUserId(1);
+            storeImg.setUpdatedUserId(1);
+            storeImg.setImgUrl(goods.getImgUrl().get(i));
+            // 保存图片 插入store_img数据库
+            storeImgMapper.insert(storeImg);
+        }
+        return true;
+    }
+
+    /**
+     * 发送消息
+     * @param goods 商品信息
+     */
+    private void sendMessage(SecondGoods goods) {
+        // 根据 goods.getUserId() 获取用户信息
+        LifeUser lifeUser = lifeUserMapper.selectById(goods.getUserId());
+        String phone = lifeUser.getUserPhone();
+        // 调取feign接口 发送消息 life_notice表
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setSenderId("system");
+        lifeNotice.setReceiverId("user_"+ phone);
+        lifeNotice.setBusinessId(goods.getAuditRecordId());
+        lifeNotice.setTitle("商品审核通知");
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("goodsId", goods.getId());
+        jsonObject.put("status", "true");
+        jsonObject.put("message", "恭喜您的商品已发布成功。");
+        lifeNotice.setContext(jsonObject.toJSONString());
+        lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
+        lifeNotice.setIsRead(0);
+        lifeNoticeMapper.insert(lifeNotice);
+        sendNotice("user_"+ phone, lifeNotice);
+    }
+
+    /**
+     * 发送通知消息
+     * @param receiverId 接收者ID
+     * @param lifeNotice 通知内容
+     */
+    private void sendNotice(String receiverId, LifeNotice lifeNotice) {
+        try {
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId("system");
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+            alienStoreFeign.sendMsgToClientByPhoneId(receiverId, JSONObject.from(webSocketVo).toJSONString());
+        } catch (Exception e) {
+            log.error("发送消息通知失败,receiverId: {}", receiverId, e);
+        }
+    }
+
+    /**
+     * 发送审核失败消息
+     * @param goods 商品审核信息
+     */
+    private void sendFailedMsg(SecondGoods goods) {
+        // 根据 goods.getUserId() 获取用户信息
+        LifeUser lifeUser = lifeUserMapper.selectById(goods.getUserId());
+        String phone = lifeUser.getUserPhone();
+        // 调取feign接口 发送消息 life_notice表
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setSenderId("system");
+        lifeNotice.setReceiverId("user_"+ phone);
+        lifeNotice.setBusinessId(goods.getAuditRecordId());
+        lifeNotice.setTitle("商品审核通知");
+        // TODO: 失败原因本期为固定文案,实际原因暂不保存
+
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("goodsId", goods.getId());
+        jsonObject.put("status", "false");
+        jsonObject.put("message", "抱歉您的商品发布失败,图片或文字存在违规行为,请您修改后重新发布。");
+        lifeNotice.setContext(jsonObject.toJSONString());
+        lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
+        lifeNotice.setIsRead(0);
+        lifeNoticeMapper.insert(lifeNotice);
+        sendNotice("user_"+ phone, lifeNotice);
+    }
+
+
+    /**
+     * 判断并获取商品封面图(若第一个链接是视频,则取第一帧)
+     * @param goodsVo imgUrl 商品图片/视频链接列表
+     * @return 封面图URL或处理后的图像标识
+     */
+    public String getCoverImageFromVideoOrImage(SecondGoodsVo goodsVo) {
+        if (goodsVo.getImgUrl() == null || goodsVo.getImgUrl().isEmpty()) {
+            return null;
+        }
+
+        String firstUrl = goodsVo.getImgUrl().get(0);
+        if (isVideoUrl(firstUrl)) {
+            // 如果是视频,获取第一帧作为封面图
+            return goodsVo.getHomeImage();
+//            return extractFirstFrameAsCover(firstUrl);
+        } else {
+            // 否则直接返回第一个图片链接
+            return firstUrl;
+        }
+    }
+
+    /**
+     * 判断URL是否为视频地址(支持常见格式)
+     * @param url 输入URL
+     * @return 是否为视频地址
+     */
+    private boolean isVideoUrl(String url) {
+        if (url == null) return false;
+        url = url.toLowerCase();
+        return url.endsWith(".mp4") ||
+               url.endsWith(".avi") ||
+               url.endsWith(".mov") ||
+               url.endsWith(".flv") ||
+               url.endsWith(".wmv") ||
+               url.endsWith(".mkv");
+    }
+
+    /**
+     * 提取视频第一帧作为封面图(需根据实际环境实现)
+     * @param videoUrl 视频地址
+     * @return 封面图URL或Base64编码字符串
+     */
+    private String extractFirstFrameAsCover(String videoUrl) {
+        // 示例返回占位符
+        return videoUtils.getImg(videoUrl);
+    }
+
+    /**
+     * 获取热销商品列表(前10)
+     * @return 热销商品列表
+     */
+    @Override
+    public List<SecondGoods> getHotSellingRankingTop10() {
+        return secondGoodsMapper.getHotSellingRankingTop10();
+    }
+
+    /**
+     * 获取收藏商品列表(前10)
+     * @return 收藏商品列表
+     */
+    @Override
+    public List<SecondGoods> getCollectTop10() {
+        return secondGoodsMapper.getCollectTop10();
+    }
+
+    /**
+     * 获取热销商品分页列表
+     * @param page 分页参数
+     * @return 热销商品分页列表
+     */
+    @Override
+    public IPage<SecondGoods> getHotSellingRanking(IPage<SecondGoods> page) {
+        return secondGoodsMapper.getHotSellingRanking(page);
+    }
+
+    /**
+     * 根据屏蔽类型获取屏蔽商品列表
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @param shieldType 屏蔽类型
+     * @return 屏蔽商品分页列表
+     */
+    @Override
+    public IPage<SecondGoods> getShieldedGoodsListByType(IPage<SecondGoods> page, Integer userId, Integer shieldType) {
+        return secondGoodsMapper.getShieldedGoodsListByType(page, userId, shieldType);
+    }
+
+    /**
+     * 搜索商品列表
+     *
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @param secondGoodsVo 搜索参数
+     * @return 商品列表
+     */
+    @Override
+    public IPage<SecondGoodsVo> searchGoodsList(IPage<SecondGoodsVo> page,Integer userId, SecondGoodsVo secondGoodsVo) {
+        // 获取商品屏蔽列表
+        List<SecondGoods> shieldedGoodsList = getShieldedGoodsList(userId);
+        // 提取屏蔽商品ID
+        List<Integer> shieldedGoodsIds = shieldedGoodsList.stream()
+                .map(SecondGoods::getId)
+                .collect(Collectors.toList());
+        if (CollectionUtil.isEmpty(shieldedGoodsIds)) {
+            shieldedGoodsIds = Collections.emptyList();
+        }
+        // 获取拉黑列表
+        List<Integer> userIdList = lifeBlacklistMapper.getBlackList(userId);
+        if (CollectionUtil.isEmpty(userIdList)) {
+            userIdList = Collections.emptyList();
+        }
+        secondGoodsVo.setShieldedGoodsIds(shieldedGoodsIds);
+        secondGoodsVo.setUserIdList(userIdList);
+
+        // 获取搜索结果分页列表
+        IPage<SecondGoodsVo> searchGoodsList = getSecondGoodsVoIPage(page, secondGoodsVo, shieldedGoodsIds, userIdList);
+
+        // 判空
+        if (CollectionUtil.isNotEmpty(searchGoodsList.getRecords())) {
+            // 批量设置商品图片信息
+//            batchSetGoodsImages(searchGoodsList);
+            // 批量设置用户信息
+            batchSetSearchUserInfo(searchGoodsList);
+            // 批量设置收藏状态
+            batchSetCollectStatus(searchGoodsList,secondGoodsVo.getUserPhone(), userId);
+            // 批量设置点赞状态
+            batchSetLikeStatus(searchGoodsList,secondGoodsVo.getUserPhone(), userId);
+        }
+        return searchGoodsList;
+    }
+
+    /**
+     * 查询搜索结果
+     * @param page 分页参数
+     * @param secondGoodsVo 查询参数
+     * @param shieldedGoodsIds 屏蔽商品id
+     * @param userIdList 屏蔽用户id
+     * @return IPage<SecondGoodsVo> 搜索结果
+     */
+    private IPage<SecondGoodsVo> getSecondGoodsVoIPage(IPage<SecondGoodsVo> page, SecondGoodsVo secondGoodsVo, List<Integer> shieldedGoodsIds, List<Integer> userIdList) {
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.notIn(CollectionUtil.isNotEmpty(shieldedGoodsIds), "sg.id", shieldedGoodsIds)
+                .notIn(CollectionUtil.isNotEmpty(userIdList), "sg.user_id", userIdList)
+                .eq("sg.goods_status", SecondGoodsStatusEnum.LISTED.getCode())// 3-上架
+                .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED);
+        // 添加对 searchData 的模糊查询
+        if (!StringUtils.isEmpty(secondGoodsVo.getSearchData())) {
+            String searchData = "%" + secondGoodsVo.getSearchData() + "%";
+            queryWrapper.and(wrapper -> wrapper
+                    .like("sg.title", searchData)
+//                .or()
+//                .like("sg.label", searchData)
+//                .or()
+//                .like("sg.description", searchData)
+//                .or()
+//                .like("sgc1.category_name", searchData)
+//                .or()
+//                .like("sgc2.category_name", searchData)
+//                .or()
+//                .like("sg.topic", searchData)
+            );
+        }
+        // 添加对 orderData 的排序 若不为空
+        if (!StringUtils.isEmpty(secondGoodsVo.getOrderData()) && secondGoodsVo.getOrderType() != null) {
+            // 正序
+            if (secondGoodsVo.getOrderType() == 1){
+                queryWrapper.orderByAsc(secondGoodsVo.getOrderData())
+                        .orderByAsc("distance");
+            }
+            // 倒序
+            if (secondGoodsVo.getOrderType() == 2){
+                queryWrapper.orderByDesc(secondGoodsVo.getOrderData())
+                        .orderByAsc("distance");
+            }
+        }else {
+            queryWrapper.orderByAsc("distance");
+        }
+
+        // 添加对 releaseTime 的查询
+        if (secondGoodsVo.getReleaseTime() != null) {
+            queryWrapper.eq("sg.release_time", secondGoodsVo.getReleaseTime());
+        }
+
+        // 返回分页结果
+        IPage<SecondGoodsVo> searchGoodsList = secondGoodsMapper.searchGoodsList(page, secondGoodsVo.getCurrentLatitude(), secondGoodsVo.getCurrentLongitude() ,queryWrapper);
+        searchGoodsList.getRecords().forEach(secondGoods -> {
+            secondGoods.setPosition(secondGoods.getSearchData());
+        });
+        return searchGoodsList;
+    }
+
+    /**
+     * 批量设置收藏状态
+     * @param searchGoodsList 搜索结果列表
+     * @param userId 用户ID(登录用户)
+     * @param phone 手机号
+     */
+    private void batchSetLikeStatus(IPage<SecondGoodsVo> searchGoodsList, String phone, Integer userId) {
+        // 批量设置点赞状态
+        searchGoodsList.getRecords().forEach(goods -> {
+            if (userId != null) {
+                // 设置点赞状态
+                LambdaUpdateWrapper<LifeLikeRecord> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(LifeLikeRecord::getHuifuId, goods.getId())
+                        .eq(LifeLikeRecord::getDianzanId, "user_"+phone).
+                        eq(LifeLikeRecord::getType, Constants.LikeType.SECOND_HAND_GOODS); // 6-二手商品
+                if (lifeLikeRecordMapper.selectCount(updateWrapper) > 0) {
+                    goods.setLikeStatus(1);
+                }
+            }
+        });
+    }
+
+    /**
+     * 批量设置收藏状态
+     * @param searchGoodsList 搜索结果列表
+     * @param userId 用户ID(登录用户)
+     * @param phone 手机号
+     */
+    private void batchSetCollectStatus(IPage<SecondGoodsVo> searchGoodsList,String phone, Integer userId) {
+        // 批量设置收藏状态
+        searchGoodsList.getRecords().forEach(goods -> {
+            if (userId != null) {
+                LambdaUpdateWrapper<LifeCollect> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(LifeCollect::getUserId, "user_"+phone)
+                        .eq(LifeCollect::getBusinessType, Constants.CollectBusinessType.DEFAULT)
+                        .eq(LifeCollect::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED)
+                        .eq(LifeCollect::getBusinessId, goods.getId());
+                        if (lifeCollectMapper.selectCount(updateWrapper) > 0) {
+                            goods.setCollectStatus(1);
+                        }
+            }
+        });
+    }
+
+    /**
+     * 获取商品屏蔽列表
+     * @param userId 用户ID
+     * @return 商品屏蔽列表
+     */
+    private List<SecondGoods> getShieldedGoodsList(Integer userId) {
+        // 调用mapper方法
+        return secondGoodsMapper.getShieldedGoodsList(userId);
+    }
+
+    /**
+     * 获取商品详情
+     * @param id 商品ID
+     * @return 商品详情
+     */
+    @Override
+    public SecondGoodsVo getSecondGoodsById(Integer id) {
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("sg.id", id)
+                .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED);
+        SecondGoodsVo secondGoods = secondGoodsMapper.getGoodsDetails(queryWrapper);
+        // 设置图片信息
+        QueryWrapper<StoreImg> query = new QueryWrapper<>();
+        query.lambda()
+                .eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_GOODS) // 商品 图片
+                .eq(StoreImg::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED)
+                .in(StoreImg::getStoreId, id);
+        List<StoreImg> storeImgs = storeImgMapper.selectList(query);
+        // 设置图片信息
+        if (CollectionUtil.isNotEmpty(storeImgs)) {
+            secondGoods.setImgUrl(storeImgs.stream().map(StoreImg::getImgUrl).collect(Collectors.toList()));
+        }
+        return secondGoods;
+    }
+
+    /**
+     * 获取屏蔽商品分页列表
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @return 屏蔽商品分页列表
+     */
+    @Override
+    public IPage<SecondGoodsVo> getShieldedGoodsPage(IPage<SecondGoodsVo> page, Integer userId) {
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper
+                // 可以查看已删除的商品数据
+//                .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("ss.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("ss.user_id", userId)
+                .eq("ss.shield_type", 1)
+                .orderByDesc("ss.created_time");
+        return secondGoodsMapper.getShieldedGoodsPage(page, queryWrapper);
+
+    }
+
+    /**
+     * 获取收藏商品列表
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @return IPage<SecondGoodsVo> 收藏商品列表
+     */
+    @Override
+    public IPage<SecondGoodsVo> getCollectGoodsPage(IPage<SecondGoodsVo> page, int userId) {
+        LifeUser lifeUser = lifeUserMapper.selectById(userId);
+        // 获取商品屏蔽列表
+        List<SecondGoods> shieldedGoodsList = getShieldedGoodsList(userId);
+        // 提取屏蔽商品ID
+        List<Integer> shieldedGoodsIds = shieldedGoodsList.stream()
+                .map(SecondGoods::getId)
+                .collect(Collectors.toList());
+
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper
+                // 可以查看已删除的商品数据
+//                .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("lc.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .notIn(CollectionUtil.isNotEmpty(shieldedGoodsIds), "sg.id", shieldedGoodsIds)
+                .eq("lc.user_id", "user_"+lifeUser.getUserPhone())
+                .orderByDesc("lc.created_time");
+        return secondGoodsMapper.getCollectGoodsPage(page, queryWrapper);
+    }
+
+    /**
+     * 获取购买商品列表
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @return IPage<SecondGoodsVo> 购买商品列表
+     */
+    @Override
+    public IPage<SecondGoodsVo> getBuyGoodsPage(IPage<SecondGoodsVo> page, int userId) {
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper
+                // 可以查看已删除的商品数据
+//                .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("str.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("str.buyer_id", userId) // 买家ID
+                .eq("sg.goods_status", SecondGoodsStatusEnum.SOLD.getCode()) // 5-已售出
+                .eq("str.trade_status", Constants.TradeStatus.TRADE_SUCCESS) // 4-交易成功
+                .orderByDesc("str.created_time");
+        return secondGoodsMapper.getBuyGoodsPage(page, queryWrapper);
+    }
+
+    /**
+     * 根据商品状态获取商品列表 - 0:草稿 1:审核中 2:审核失败 3:已上架 4:已下架 5:已售出
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @return IPage<SecondGoodsVo> 出售商品列表
+     */
+    @Override
+    public IPage<SecondGoodsVo> getSellGoodsPage(IPage<SecondGoodsVo> page, SecondGoodsVo secondGoodsVo,int userId) {
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("sg.user_id", userId)
+                .eq("sg.goods_status", secondGoodsVo.getGoodsStatus()); // 5-已售出 ,0-草稿
+        return secondGoodsMapper.getSellGoodsPage(page, queryWrapper);
+    }
+
+    /**
+     * 获取用户商品列表
+     * @param page 分页参数
+     * @param userId 主页用户id
+     * @return IPage<SecondGoodsVo> 用户商品列表
+     */
+    @Override
+    public IPage<SecondGoodsVo> getUserGoodsPage(IPage<SecondGoodsVo> page, Double currentLatitude , Double currentLongitude, Integer userId) {
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("sg.user_id", userId) // 主页用户ID
+                .in("sg.goods_status", SecondGoodsStatusEnum.LISTED.getCode(), SecondGoodsStatusEnum.SOLD.getCode()) // 3-上架 5-已售出
+                .orderByAsc("FIELD(sg.goods_status, 3, 5)")
+                .orderByDesc("sg.release_time");
+        return secondGoodsMapper.getUserGoodsPage(page, currentLatitude, currentLongitude, queryWrapper);
+    }
+
+    /**
+     * 获取我的商品列表
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @return IPage<SecondGoodsVo> 我的商品列表
+     */
+    @Override
+    public IPage<SecondGoodsVo> getMyGoodsPage(IPage<SecondGoodsVo> page, int userId) {
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("sg.user_id", userId) // 用户ID
+                .in("sg.goods_status", SecondGoodsStatusEnum.REVIEW_FAILED.getCode(), 
+                        SecondGoodsStatusEnum.DELISTED.getCode(), 
+                        SecondGoodsStatusEnum.SOLD.getCode(), 
+                        SecondGoodsStatusEnum.LISTED.getCode()) // 3-上架 5-已售出
+                .orderByAsc("FIELD(sg.goods_status, 2, 4, 5, 3)")
+                .orderByDesc("sg.release_time");
+        return secondGoodsMapper.getMyGoodsPage(page, queryWrapper);
+    }
+
+    /**
+     * 获取草稿列表
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @return IPage<SecondGoodsVo> 草稿列表
+     */
+    @Override
+    public IPage<SecondGoodsVo> getDraftList(IPage<SecondGoodsVo> page, int userId) {
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("sg.user_id", userId) // 用户ID
+                .eq("sg.goods_status", SecondGoodsStatusEnum.DRAFT.getCode())
+                .orderByDesc("sg.created_time");
+        return secondGoodsMapper.getDraftList(page, queryWrapper);
+    }
+
+    /**
+     * 获取点赞商品列表
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @param phone 手机号
+     * @return IPage<SecondGoodsVo> 点赞商品列表
+     */
+    @Override
+    public IPage<SecondGoodsVo> getLikeGoodsPage(IPage<SecondGoodsVo> page, int userId, String phone) {
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper
+                // 可以查看已删除的商品数据
+//                .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("lc.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("lc.dianzan_id", "user_"+phone)
+                .eq("lc.type", Constants.LikeType.SECOND_HAND_GOODS) // 6-二手商品
+                .eq("sg.user_id", userId) // 用户ID
+                .orderByDesc("lc.created_time");
+        return secondGoodsMapper.getLikeGoodsPage(page, queryWrapper);
+    }
+
+    /**
+     * 获取交易列表
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @return IPage<SellGoodsVo> 交易列表
+     */
+    @Override
+    public IPage<SellGoodsVo> getTransactionList(IPage<SellGoodsVo> page, Integer userId) {
+        IPage<SellGoodsVo> result = new Page<>();
+        QueryWrapper<SellGoodsVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper
+                // 可以查看已删除的商品数据
+//                .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .eq("str.delete_flag", Constants.DeleteFlag.NOT_DELETED)
+                .and(wrapper -> wrapper.eq("str.buyer_id", userId)
+                        .or()
+                        .eq("str.seller_id", userId))
+                .orderByDesc("str.created_time");
+        result = secondGoodsMapper.getTransactionList(page, userId, queryWrapper);
+        // 批量设置用户信息
+        batchSetSellUserInfo(result);
+        return result;
+    }
+
+    /**
+     * 根据用户ID和商品状态获取商品列表
+     * @param userId 用户ID
+     * @param goodsStatus 商品状态
+     * @return 商品列表
+     */
+    @Override
+    public List<SecondGoods> getGoodsListByUserId(Integer userId, Integer goodsStatus) {
+        // 获取商品屏蔽列表
+        List<SecondGoods> shieldedGoodsList = getShieldedGoodsList(userId);
+        // 提取屏蔽商品ID
+        List<Integer> shieldedGoodsIds = shieldedGoodsList.stream()
+                .map(SecondGoods::getId)
+                .collect(Collectors.toList());
+
+        LambdaQueryWrapper<SecondGoods> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SecondGoods::getUserId, userId);
+        queryWrapper.eq(SecondGoods::getGoodsStatus, goodsStatus);
+        queryWrapper.notIn(CollectionUtil.isNotEmpty(shieldedGoodsIds), SecondGoods::getId, shieldedGoodsIds);
+        queryWrapper.orderByDesc(SecondGoods::getReleaseTime);
+        return secondGoodsMapper.selectList(queryWrapper);
+    }
+
+    /**
+     * 处理视频审核结果
+     * @param task 视频审核任务
+     */
+    @Override
+    public void processVideoModerationResult(SecondVideoTask task) {
+        try {
+            // 查找关联的商品
+            QueryWrapper<SecondGoods> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("video_task_id", task.getTaskId());
+            SecondGoods goods = getOne(queryWrapper);
+            
+            if (goods == null) {
+                log.warn("未找到关联的商品,任务ID: {}", task.getTaskId());
+                return;
+            }
+            
+            // 根据审核结果更新商品状态
+            if ("none".equals(task.getRiskLevel())) {
+                log.warn("视频审核通过,任务ID: {}", task.getTaskId());
+                SecondGoodsVo secondGoodsVo = new SecondGoodsVo();
+                BeanUtils.copyProperties(goods, secondGoodsVo);
+                QueryWrapper<StoreImg> imgQueryWrapper = new QueryWrapper<>();
+                imgQueryWrapper.eq("store_id", goods.getId());
+                imgQueryWrapper.eq("img_type", 18);
+                List<StoreImg> storeImgs = storeImgMapper.selectList(imgQueryWrapper);
+                List<String> imgUrls = storeImgs.stream()
+                        .map(StoreImg::getImgUrl)
+                        .filter(imgUrl -> StringUtils.isNotBlank(imgUrl))
+                        .collect(Collectors.toList());
+                secondGoodsVo.setImgUrl(imgUrls);
+
+                secondGoodsAuditService.performSecondRoundReview(goods, secondGoodsVo);
+//
+//                // 审核通过
+//                goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode());
+//                goods.setFailedReason("");
+//                goods.setReleaseTime(new Date());
+//                updateById(goods);
+//
+//                // 更新审核记录
+//                createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+//
+//                // 发送审核成功消息
+//                sendMessage(goods);
+//                // 审核成功,记录操作历史
+//                // 审核成功,记录操作历史
+//                String operationName = "";
+//                QueryWrapper<SecondGoodsRecord> queryRecordWrapper = new QueryWrapper<>();
+//                queryRecordWrapper.eq("goods_id", goods.getId());
+//                log.info("查询操作记录开始 goods_id: {}", goods.getId());
+//                List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryRecordWrapper);
+//                log.info("查询操作记录结束 recordList: {}", recordList);
+//                if (CollectionUtil.isNotEmpty(recordList)){
+//                    operationName = "重新发布";
+//                }else {
+//                    operationName = "首次发布";
+//                }
+//                recordGoodsOperation(goods, operationName);
+//
+//                // 检查用户是否在24小时内发布同类商品超过阈值
+//                if (!checkUserPublishSameCategoryLimit(goods)) {
+//                    log.warn("用户 {} 在24小时内发布同类商品次数超过限制", goods.getUserId());
+//                }
+//
+//                // 检查用户是否在24小时内发布商品超过阈值
+//                if (!checkUserPublishLimit(goods)) {
+//                    log.warn("用户 {} 在24小时内发布商品次数超过限制", goods.getUserId());
+//
+//                }
+            } else {
+                // 审核不通过
+                goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+
+                // 解析审核结果,生成具体的失败原因
+                String failedReason = parseVideoModerationFailureReason(task);
+                goods.setFailedReason(failedReason);
+                updateById(goods);
+
+                // 更新审核记录
+                createGoodsAudit(goods, failedReason, Constants.AuditStatus.FAILED);
+
+                // 记录操作历史
+                recordGoodsOperation(goods, "视频审核失败");
+                // 发送审核失败消息
+                sendFailedMsg(goods);
+            }
+        } catch (Exception e) {
+            log.error("处理视频审核结果时发生异常,任务ID: {}", task.getTaskId(), e);
+        }
+    }
+    
+    /**
+     * 解析视频审核失败原因
+     * @param task 视频审核任务
+     * @return 失败原因
+     */
+    private String parseVideoModerationFailureReason(SecondVideoTask task) {
+        StringBuilder reasonBuilder = new StringBuilder("视频审核不通过,风险等级: " + task.getRiskLevel());
+        
+        try {
+            // 解析审核结果JSON
+            JSONObject resultJson = JSON.parseObject(task.getResult());
+            if (resultJson != null && resultJson.containsKey("data")) {
+                JSONObject data = resultJson.getJSONObject("data");
+                
+                // 处理帧结果(视频画面)
+                if (data.containsKey("FrameResult")) {
+                    JSONObject frameResult = data.getJSONObject("FrameResult");
+                    if (frameResult != null && frameResult.containsKey("FrameSummarys")) {
+                        JSONArray frameSummarys = frameResult.getJSONArray("FrameSummarys");
+                        if (frameSummarys != null && !frameSummarys.isEmpty()) {
+                            reasonBuilder.append("。检测到违规内容:");
+                            for (int i = 0; i < frameSummarys.size(); i++) {
+                                JSONObject summary = frameSummarys.getJSONObject(i);
+                                String label = summary.getString("Label");
+                                String description = summary.getString("Description");
+                                Integer labelSum = summary.getInteger("LabelSum");
+                                
+                                if (label != null && !label.isEmpty()) {
+                                    reasonBuilder.append("[").append(description != null ? description : label)
+                                            .append("]出现").append(labelSum != null ? labelSum : 1).append("次;");
+                                }
+                            }
+                        }
+                    }
+                }
+                
+                // 处理音频结果
+                if (data.containsKey("AudioResult")) {
+                    JSONObject audioResult = data.getJSONObject("AudioResult");
+                    if (audioResult != null && audioResult.containsKey("AudioSummarys")) {
+                        JSONArray audioSummarys = audioResult.getJSONArray("AudioSummarys");
+                        if (audioSummarys != null && !audioSummarys.isEmpty()) {
+                            reasonBuilder.append("。检测到违规音频:");
+                            for (int i = 0; i < audioSummarys.size(); i++) {
+                                JSONObject summary = audioSummarys.getJSONObject(i);
+                                String label = summary.getString("Label");
+                                String description = summary.getString("Description");
+                                Integer labelSum = summary.getInteger("LabelSum");
+                                
+                                if (label != null && !label.isEmpty()) {
+                                    reasonBuilder.append("[").append(description != null ? description : label)
+                                            .append("]出现").append(labelSum != null ? labelSum : 1).append("次;");
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.warn("解析视频审核结果失败,使用默认原因,任务ID: {}", task.getTaskId(), e);
+        }
+        
+        return reasonBuilder.toString();
+    }
+
+    /**
+     * 批量设置用户信息(用于SecondGoodsVo列表)
+     * @param searchGoodsList 搜索结果
+     */
+    private void batchSetSearchUserInfo(IPage<SecondGoodsVo> searchGoodsList) {
+        batchSetUserInfo(searchGoodsList, 
+                        SecondGoodsVo::getUserId,
+                        (goods, userInfo) -> {
+                            goods.setUserName(userInfo.getUserName());
+                            goods.setRealName(userInfo.getRealName());
+                            goods.setUserImage(userInfo.getUserImage());
+                            goods.setUserId(userInfo.getId());
+                            goods.setUserPhone("user_"+userInfo.getUserPhone());
+                        });
+    }
+    
+    /**
+     * 批量设置用户信息(用于SellGoodsVo列表)
+     * @param sellGoodsList 搜索结果
+     */
+    private void batchSetSellUserInfo(IPage<SellGoodsVo> sellGoodsList) {
+        batchSetUserInfo(sellGoodsList, 
+                        SellGoodsVo::getUserId,
+                        (goods, userInfo) -> {
+                            goods.setUserName(userInfo.getUserName());
+                            goods.setRealName(userInfo.getRealName());
+                            goods.setUserImage(userInfo.getUserImage());
+                            goods.setUserId(userInfo.getId());
+                            goods.setUserPhone(userInfo.getUserPhone());
+                        });
+    }
+
+    /**
+     * 批量设置商品图片信息
+     * @param searchGoodsList 商品列表
+     */
+    private void batchSetGoodsImages(IPage<SecondGoodsVo> searchGoodsList) {
+        // 批量获取图片信息
+        if (CollectionUtil.isNotEmpty(searchGoodsList.getRecords())) {
+            List<Integer> goodsIds = searchGoodsList.getRecords().stream()
+                    .map(SecondGoodsVo::getId)
+                    .collect(Collectors.toList());
+            // 批量获取图片信息
+            QueryWrapper<StoreImg> queryWrapper = new QueryWrapper<>();
+            queryWrapper.lambda()
+                    .eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_GOODS) // 商品 图片
+                    .eq(StoreImg::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED)
+                    .in(StoreImg::getStoreId, goodsIds);
+            List<StoreImg> imagesList= storeImgMapper.getImgsByGoodsIds(queryWrapper);
+            // 集合根據商品id进行分组返回map
+            Map<Integer, List<StoreImg>> imagesMap = imagesList.stream().collect(Collectors.groupingBy(StoreImg::getStoreId));
+            // 遍历
+            for (SecondGoodsVo goods : searchGoodsList.getRecords()) {
+                // 提取图片url
+                List<StoreImg> images = imagesMap.get(goods.getId());
+                if (images != null) {
+                    goods.setImgUrl(images.stream()
+                            .map(StoreImg::getImgUrl)
+                            .collect(Collectors.toList()));
+                }
+            }
+        }
+    }
+
+    /**
+     * 批量设置商品图片信息(用于管理后台)
+     * @param goodsList 商品列表
+     */
+    private void batchSetGoodsImagesForAdmin(IPage<SecondGoodsVo> goodsList) {
+        // 批量获取图片信息
+        if (CollectionUtil.isNotEmpty(goodsList.getRecords())) {
+            List<Integer> goodsIds = goodsList.getRecords().stream()
+                    .map(SecondGoodsVo::getId)
+                    .collect(Collectors.toList());
+                    // 批量获取图片信息
+                    QueryWrapper<StoreImg> queryWrapper = new QueryWrapper<>();
+                    queryWrapper.lambda()
+                            .eq(StoreImg::getImgType, Constants.ImageType.SECOND_HAND_GOODS) // 商品 图片
+                            .eq(StoreImg::getDeleteFlag, Constants.DeleteFlag.NOT_DELETED)
+                            .in(StoreImg::getStoreId, goodsIds);
+                    List<StoreImg> imagesList= storeImgMapper.getImgsByGoodsIds(queryWrapper);
+                    // 集合根據商品id进行分组返回map
+                    Map<Integer, List<StoreImg>> imagesMap = imagesList.stream().collect(Collectors.groupingBy(StoreImg::getStoreId));
+                    // 遍历
+                    for (SecondGoodsVo goods : goodsList.getRecords()) {
+                        // 提取图片url
+                        List<StoreImg> images = imagesMap.get(goods.getId());
+                        if (images != null) {
+                            goods.setImgUrl(images.stream()
+                                    .map(StoreImg::getImgUrl)
+                                    .collect(Collectors.toList()));
+
+                        }
+                        
+                        // 设置状态名称
+                        if (goods.getDeleteFlag() != null && goods.getDeleteFlag().equals(Constants.DeleteFlag.DELETED)) {
+                            goods.setGoodsStatusName("已删除");
+                        } else if (goods.getGoodsStatus() != null) {
+                            SecondGoodsStatusEnum statusEnum = SecondGoodsStatusEnum.fromCode(goods.getGoodsStatus());
+                            if (statusEnum != null) {
+                                goods.setGoodsStatusName(statusEnum.getDescription());
+                            }
+                        }
+                    }
+        }
+    }
+
+
+    /**
+     * 批量设置用户信息
+     * @param <T> 任意商品VO类型
+     * @param goodsList 商品列表
+     * @param userIdMapper 从商品VO获取用户ID的函数
+     * @param userSetter 从用户信息设置到商品VO的函数
+     */
+    private <T> void batchSetUserInfo(IPage<T> goodsList,
+                                      java.util.function.Function<T, Integer> userIdMapper,
+                                      java.util.function.BiConsumer<T, LifeUserVo> userSetter) {
+        if (CollectionUtil.isNotEmpty(goodsList.getRecords())) {
+            List<Integer> userIds = goodsList.getRecords().stream()
+                    .map(userIdMapper)
+                    .collect(Collectors.toList());
+
+            QueryWrapper<LifeUserVo> queryWrapper = new QueryWrapper<>();
+            queryWrapper.in("id", userIds)
+                    .eq("delete_flag", Constants.DeleteFlag.NOT_DELETED);
+
+            List<LifeUserVo> userInfoList = lifeUserMapper.getUserByIds(queryWrapper);
+            Map<Integer, LifeUserVo> userInfoMap = userInfoList.stream()
+                    .collect(Collectors.toMap(LifeUserVo::getId, lifeUserVo -> lifeUserVo));
+
+            for (T goods : goodsList.getRecords()) {
+                LifeUserVo userInfo = userInfoMap.get(userIdMapper.apply(goods));
+                if (userInfo != null && userSetter != null) {
+                    userSetter.accept(goods, userInfo);
+                }
+            }
+        }
+    }
+
+    /**
+     * 获取管理后台商品列表
+     * @param page 分页参数
+     * @param queryDTO 查询参数DTO
+     * @return 商品列表
+     */
+    @Override
+    public IPage<SecondGoodsVo> getAdminGoodsList(IPage<SecondGoodsVo> page, SecondGoodsAdminQueryDTO queryDTO) {
+        // 构建查询条件
+        QueryWrapper<SecondGoodsVo> queryWrapper = new QueryWrapper<>();
+        
+        // 商品名称模糊查询
+        queryWrapper.like(org.apache.commons.lang3.StringUtils.isNotBlank(queryDTO.getTitle()), "sg.title", queryDTO.getTitle());
+        // 过滤草稿
+        queryWrapper.ne("sg.goods_status",SecondGoodsStatusEnum.DRAFT.getCode());
+        // 状态查询分为两种情况处理
+        if (queryDTO.getGoodsStatus() != null) {
+            if (queryDTO.getGoodsStatus() == 6) {
+                // 当状态为6时,查询删除标记为1的数据
+                queryWrapper.eq("sg.delete_flag", Constants.DeleteFlag.DELETED);
+            } else {
+                // 其他状态按照商品状态进行查询,并且删除标记为0
+                queryWrapper.eq("sg.goods_status", queryDTO.getGoodsStatus())
+                        .eq("sg.delete_flag", Constants.DeleteFlag.NOT_DELETED);
+            }
+        }
+        
+        // 发布时间范围查询
+        queryWrapper.ge(queryDTO.getReleaseStartTime() != null, "sg.release_time", queryDTO.getReleaseStartTime())
+                   .le(queryDTO.getReleaseEndTime() != null, "sg.release_time", queryDTO.getReleaseEndTime());
+        queryWrapper.orderByDesc("sg.id");
+        // 查询数据
+        IPage<SecondGoodsVo> result = secondGoodsMapper.getAdminGoodsList(page, queryWrapper);
+        
+        // 批量设置商品图片信息
+        batchSetGoodsImagesForAdmin(result);
+        
+        return result;
+    }
+
+    /**
+     * 根据风控记录中的JSON数据批量下架商品
+     * @param goodsIds 商品ID集合
+     * @return 是否下架成功
+     */
+    public boolean batchShelveGoodsByIds(List<Integer> goodsIds) {
+        if (goodsIds == null || goodsIds.isEmpty()) {
+            return true;
+        }
+        
+        // 批量更新商品状态为下架状态
+        LambdaUpdateWrapper<SecondGoods> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.in(SecondGoods::getId, goodsIds)
+                .set(SecondGoods::getGoodsStatus, SecondGoodsStatusEnum.DELISTED.getCode());
+        
+        boolean updateResult = update(updateWrapper);
+        
+        if (updateResult) {
+            // 批量查询更新后的商品信息
+            Collection<SecondGoods> updatedGoodsList = listByIds(goodsIds);
+            
+            // 为每个商品记录操作历史并发送通知
+            for (SecondGoods goods : updatedGoodsList) {
+                recordGoodsOperation(goods, "风控下架");
+                sendShelveMessage(goods);
+            }
+        }
+        
+        return updateResult;
+    }
+
+    /**
+     * 根据风控记录中的JSON数据批量下架商品
+     * @param ruleType 风控规则类型
+     * @param businessId 业务ID
+     * @return 是否下架成功
+     */
+    @Override
+    public boolean batchShelveGoodsByRiskControlRecord(Integer ruleType, String businessId) {
+        try {
+            List<Integer> goodsIdList = Lists.newArrayList();
+            // 获取相同类型的风控数据
+            List<SecondRiskControlRecord> riskControlRecordList = riskControlService.getSameTypeRiskControlRecords(ruleType, businessId);
+            // 获取风控数据详情
+            List<String> detailInfoList = riskControlRecordList.stream()
+                    .map(SecondRiskControlRecord::getDetailInfo)
+                    .collect(Collectors.toList());
+
+            // 声明所有被风控的商品ID
+            List<Integer> idList = Lists.newArrayList();
+
+            // 循环情信息json 转换为集合
+            for (String detailInfo : detailInfoList){
+                // 解析JSON获取商品ID列表
+                List<Integer> ids = JSON.parseArray(detailInfo, Integer.class);
+                idList.addAll(ids);
+            }
+            // goodsIdList 去重
+            idList = idList.stream().distinct().collect(Collectors.toList());
+            // 交易欺诈
+            if (ruleType.equals(RiskControlRuleTypeEnum.TRANSACTION_FRAUD.getRuleType())){
+                // 根据商品记录id集合查询商品Id 集合
+                QueryWrapper<SecondGoodsRecord> queryWrapper = new QueryWrapper<>();
+                queryWrapper.in("id", idList);
+                List<SecondGoodsRecord> goodsRecordList = secondGoodsRecordMapper.selectList(queryWrapper);
+                // 获取商品Id集合
+                List<Integer> goodsIds = goodsRecordList.stream().map(SecondGoodsRecord::getGoodsId).collect(Collectors.toList());
+                // 去重
+                goodsIds = goodsIds.stream().distinct().collect(Collectors.toList());
+                goodsIdList.addAll(goodsIds);
+                // 异常发布
+            } else if (RiskControlRuleTypeEnum.ABNORMAL_PUBLISH.getRuleType().equals(ruleType)) {
+                goodsIdList.addAll(idList);
+            }
+            // 调用批量下架方法
+            return batchShelveGoodsByIds(goodsIdList);
+        } catch (Exception e) {
+            log.error("根据风控记录批量下架商品失败,详情: {}", ruleType, e);
+            return false;
+        }
+    }
+
+    /**
+     * 下架商品
+     * @param secondGoods 商品信息
+     * @return 是否下架成功
+     */
+    @Override
+    public boolean shelveSecondGoods(SecondGoodsVo secondGoods) {
+        log.info("SecondGoodsServiceImpl.shelveSecondGoods?secondGoods={}", secondGoods.toString());
+        // 修改商品状态为4 - 已下架
+        secondGoods.setGoodsStatus(4);
+        boolean updateResult = updateById(secondGoods);
+        if (updateResult) {
+            // 获取最新的商品信息并记录操作历史
+            SecondGoods updatedGoods = getById(secondGoods.getId());
+            recordGoodsOperation(updatedGoods, "下架商品");
+            // 发送系统通知
+            sendShelveMessage(updatedGoods);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+
+    /**
+     * 发送商品下架消息
+     * @param goods 商品信息
+     */
+    private void sendShelveMessage(SecondGoods goods) {
+        try {
+            // 根据 goods.getUserId() 获取用户信息
+            LifeUser lifeUser = lifeUserMapper.selectById(goods.getUserId());
+            String phone = lifeUser.getUserPhone();
+            // 调取feign接口 发送消息 life_notice表
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setSenderId("system");
+            lifeNotice.setReceiverId("user_"+ phone);
+            lifeNotice.setBusinessId(goods.getId());
+            lifeNotice.setTitle("商品下架通知");
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("goodsId", goods.getId());
+//            jsonObject.put("status", "true");
+            jsonObject.put("message", "您的商品:"+ goods.getTitle() + "已下架");
+            lifeNotice.setContext(jsonObject.toJSONString());
+            lifeNotice.setNoticeType(Constants.Notice.SYSTEM_NOTICE); // 系统通知
+            lifeNotice.setIsRead(0);
+            lifeNoticeMapper.insert(lifeNotice);
+            sendShelveNotice("user_"+ phone, lifeNotice);
+        } catch (Exception e) {
+            log.error("发送消息通知失败,goods: {}", goods, e);
+        }
+    }
+
+    /**
+     * 发送下架通知
+     * @param receiverId 接收者ID
+     * @param lifeNotice 通知内容
+     */
+    private void sendShelveNotice(String receiverId, LifeNotice lifeNotice) {
+        try {
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId("system");
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+            alienStoreFeign.sendMsgToClientByPhoneId(receiverId, JSONObject.from(webSocketVo).toJSONString());
+        } catch (Exception e) {
+            log.error("发送消息通知失败,receiverId: {}", receiverId, e);
+        }
+    }
+
+}

+ 292 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondRecommendServiceImpl.java

@@ -0,0 +1,292 @@
+package shop.alien.second.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.second.vo.SecondCommentVo;
+import shop.alien.entity.second.vo.SecondGoodsRecommendVo;
+import shop.alien.entity.store.LifeFans;
+import shop.alien.entity.store.StoreComment;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.mapper.LifeFansMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.second.SecondRecommendMapper;
+import shop.alien.second.service.SecondRecommendService;
+import shop.alien.util.common.JwtUtil;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 二手商品服务实现类
+ */
+@Slf4j
+@Service
+public class SecondRecommendServiceImpl extends ServiceImpl<SecondRecommendMapper, SecondGoodsRecommendVo> implements SecondRecommendService {
+
+    @Autowired
+    private SecondRecommendMapper mapper;
+
+    @Autowired
+    private StoreImgMapper storeImgMapper;
+
+    @Autowired
+    private LifeFansMapper lifeFansMapper;
+    /**
+     * 获取二手商品推荐列表
+     * @param page 分页信息
+     * @return 推荐列表
+     */
+    @Override
+    public IPage<SecondGoodsRecommendVo> getSecondRecommendByPage(
+            IPage<SecondGoodsRecommendVo> page, String longitude, String latitude, Integer typeId, String radiusKm) throws Exception {
+        try{
+            JSONObject data = JwtUtil.getCurrentUserInfo();
+            Integer userId = null;
+            String phoneId = null;
+            if (data != null) {
+                userId = data.getInteger("userId");
+                phoneId = data.getString("phone");
+                log.info("获取用户ID:userId={}", userId);
+            }
+            if (userId == null) {
+                return null;
+            }
+            IPage<SecondGoodsRecommendVo> result = mapper.getSecondRecommendByPage(page, userId, longitude + "," + latitude, typeId, "user_" + phoneId, radiusKm);
+            for (SecondGoodsRecommendVo row : result.getRecords()) {
+                // 价格保留两位小数
+                row.setPrice(row.getAmount() != null ? row.getAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toString() : null);
+                // 距离拼接
+                if (StringUtil.isNotBlank(row.getDist())) {
+                    row.setPosition("距离" + row.getDist() + "km");
+                }
+            }
+            return result;
+        } catch (Exception e) {
+            log.error("SecondRecommendServiceImpl.getSecondRecommendByPage Error Mgs={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    /**
+     * 获取二手商品关注列表
+     * @param page 分页信息
+     * @return 关注列表
+     */
+    public IPage<SecondGoodsRecommendVo> querySecondConcernByPage(
+            IPage<SecondGoodsRecommendVo> page, String position) throws Exception {
+        try {
+            JSONObject data = JwtUtil.getCurrentUserInfo();
+            String phoneId = null;
+            Integer userId = null;
+            if (data != null) {
+                phoneId = data.getString("phone");
+                userId = data.getInteger("userId");
+            }
+            if (StringUtil.isBlank(phoneId)) {
+                return null;
+            }
+            IPage<SecondGoodsRecommendVo> list = mapper.querySecondConcernByPage(page, userId, "user_" + phoneId, position);
+            List<Integer> idList = list.getRecords().stream() // 创建流
+                    .map(obj -> obj.getId())   // 提取每个元素的 ID
+                    .collect(Collectors.toList());
+            if (CollectionUtil.isEmpty(idList)) {
+                return list;
+            }
+            List<SecondCommentVo> commentList =mapper.querySecondCommentInfo(idList);
+            list.getRecords().forEach(item -> {
+                // 距离拼接
+                if (StringUtil.isNotBlank(item.getDist())) {
+                    item.setPosition("距离" + item.getDist() + "km");
+                }
+                // 价格保留两位小数
+                item.setPrice(item.getAmount() != null ? item.getAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toString() : null);
+
+                // 评论列表
+                List<SecondCommentVo> cList = new ArrayList<>();
+                commentList.forEach(comment -> {
+                    if (item.getId().equals(comment.getBusinessId())) {
+                        cList.add(comment);
+                    }
+                });
+                if (cList.size() > 0) {
+                    item.setCommentList(cList);
+                }
+            });
+            return list;
+        } catch (Exception e){
+            log.error("SecondRecommendServiceImpl.querySecondConcernByPage Error Mgs={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+
+    /**
+     * 获取二手商品新品列表
+     * @param page 分页信息
+     * @return 关注列表
+     */
+    public IPage<SecondGoodsRecommendVo> querySecondNewGoodsByPage(
+            IPage<SecondGoodsRecommendVo> page, String position, String radiusKm) throws Exception {
+        try {
+            JSONObject data = JwtUtil.getCurrentUserInfo();
+            String phoneId = null;
+            String userId = null;
+            if (data != null) {
+                phoneId = data.getString("phone");
+                userId = data.getString("userId");
+            }
+            if (StringUtil.isBlank(phoneId)) {
+                return null;
+            }
+            IPage<SecondGoodsRecommendVo> list = mapper.querySecondNewGoodsByPage(page, userId,"user_" + phoneId, position, radiusKm);
+            List<Integer> idList = list.getRecords().stream() // 创建流
+                    .map(obj -> obj.getId())   // 提取每个元素的 ID
+                    .collect(Collectors.toList());
+            if (CollectionUtil.isEmpty(idList)) {
+                return list;
+            }
+            List<SecondCommentVo> commentList = mapper.querySecondCommentInfo(idList);
+            list.getRecords().forEach(item -> {
+                // 距离拼接
+                if (StringUtil.isNotBlank(item.getDist())) {
+                    item.setPosition("距离" + item.getDist() + "km");
+                }
+                // 价格保留两位小数
+                item.setPrice(item.getAmount() != null ? item.getAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toString() : null);
+
+                // 评论列表
+                List<SecondCommentVo> cList = new ArrayList<>();
+                commentList.forEach(comment -> {
+                    if (item.getId().equals(comment.getBusinessId())) {
+                        cList.add(comment);
+                    }
+                });
+                if (cList.size() > 0) {
+                    item.setCommentList(cList);
+                }
+            });
+            return list;
+        } catch (Exception e) {
+            log.error("SecondRecommendServiceImpl.querySecondNewGoodsByPage Error Mgs={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    public SecondGoodsRecommendVo querySecondGoodsDetail(Integer goodsId, String position) throws Exception {
+        try {
+            JSONObject data = JwtUtil.getCurrentUserInfo();
+            String phoneId = null;
+            if (data != null) {
+                phoneId = data.getString("phone");
+            }
+            if (StringUtil.isBlank(phoneId)) {
+                return null;
+            }
+            SecondGoodsRecommendVo item = mapper.querySecondGoodsDetail(goodsId, "user_" + phoneId, position);
+
+            if (item != null) {
+                // 设置图片信息
+                QueryWrapper<StoreImg> query = new QueryWrapper<>();
+                query.lambda()
+                        .eq(StoreImg::getImgType, 18) // 商品 图片
+                        .eq(StoreImg::getDeleteFlag, 0)
+                        .eq(StoreImg::getStoreId, goodsId);
+                List<StoreImg> storeImgs = storeImgMapper.selectList(query);
+                // 设置图片信息
+                if (storeImgs.size() > 0) {
+                    // 先按视频后缀排序,再按ImgSort排序
+                    String[] items = storeImgs.stream()
+                            .sorted((o1, o2) -> {
+                                boolean o1IsMp4 = isVideoUrl(o1.getImgUrl());
+                                boolean o2IsMp4 = isVideoUrl(o2.getImgUrl());
+                                // 如果都是.mp4或都不是.mp4,则按ImgSort排序
+                                if (o1IsMp4 && o2IsMp4 || !o1IsMp4 && !o2IsMp4) {
+                                    return o1.getImgSort().compareTo(o2.getImgSort());
+                                }
+                                // 否则,.mp4的排在前面
+                                return o1IsMp4 ? -1 : 1;
+                            }).map(StoreImg::getImgUrl).toArray(String[]::new);
+                    item.setImgList(items);
+                }
+
+                // 查看是否关注
+                QueryWrapper<LifeFans> query1 = new QueryWrapper<>();
+                query1.lambda()
+                        .eq(LifeFans::getFollowedId, item.getUserPhone()) // 商品图片
+                        .eq(LifeFans::getDeleteFlag, 0)
+                        .eq(LifeFans::getFansId, "user_" + phoneId);
+                List<LifeFans> lifeFans = lifeFansMapper.selectList(query1);
+                // 关注状态添加
+                if (lifeFans.size() > 0) {
+                    item.setFansStatus(1);
+                }
+
+                // 距离拼接
+                if (StringUtil.isNotBlank(item.getDist())) {
+                    item.setPosition("距离" + item.getDist() + "km");
+                }
+                item.setPrice(item.getAmount() != null ? item.getAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toString() : null);
+            }
+
+            return item;
+        } catch (Exception e) {
+            log.error("SecondRecommendServiceImpl.querySecondGoodsDetail Error Mgs={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    public static void main(String[] args) {
+        List<StoreImg> storeImgs = new ArrayList<>();
+        StoreImg storeImg = new StoreImg();
+        storeImg.setImgSort(4);
+        storeImg.setImgUrl("11111.mp4");
+        storeImgs.add(storeImg);
+        StoreImg storeImg1 = new StoreImg();
+        storeImg1.setImgSort(3);
+        storeImg1.setImgUrl("22222.jpg");
+        storeImgs.add(storeImg1);
+        StoreImg storeImg2 = new StoreImg();
+        storeImg2.setImgSort(2);
+        storeImg2.setImgUrl("33333.jpg");
+        storeImgs.add(storeImg2);
+        StoreImg storeImg3 = new StoreImg();
+        storeImg3.setImgSort(1);
+        storeImg3.setImgUrl("44444.mp4");
+        storeImgs.add(storeImg3);
+
+        // 先按视频后缀排序,再按ImgSort排序
+        String[] items = storeImgs.stream()
+                .sorted((o1, o2) -> {
+                    boolean o1IsMp4 = isVideoUrl(o1.getImgUrl());
+                    boolean o2IsMp4 = isVideoUrl(o2.getImgUrl());
+                    // 如果都是.mp4或都不是.mp4,则按ImgSort排序
+                    if (o1IsMp4 && o2IsMp4 || !o1IsMp4 && !o2IsMp4) {
+                        return o1.getImgSort().compareTo(o2.getImgSort());
+                    }
+                    // 否则,.mp4的排在前面
+                    return o1IsMp4 ? -1 : 1;
+                }).map(StoreImg::getImgUrl).toArray(String[]::new);
+
+        System.out.println(items);
+    }
+
+    private static boolean isVideoUrl(String url) {
+        if (url == null) return false;
+        url = url.toLowerCase();
+        return url.endsWith(".mp4") ||
+                url.endsWith(".avi") ||
+                url.endsWith(".mov") ||
+                url.endsWith(".flv") ||
+                url.endsWith(".wmv") ||
+                url.endsWith(".mkv");
+    }
+}

+ 1235 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondTradeRecordServiceImpl.java

@@ -0,0 +1,1235 @@
+package shop.alien.second.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.nacos.common.utils.CollectionUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.swagger.models.auth.In;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.config.properties.RiskControlProperties;
+import shop.alien.config.redis.BaseRedisService;
+import shop.alien.entity.result.BusinessException;
+import shop.alien.entity.second.*;
+import shop.alien.entity.second.enums.SecondUserCreditScoreEnum;
+import shop.alien.entity.second.vo.SecondEntrustUserDTO;
+import shop.alien.entity.store.*;
+import shop.alien.mapper.second.SecondRiskControlRecordMapper;
+import shop.alien.entity.second.vo.SecondTradeRecordVo;
+import shop.alien.entity.second.vo.SellerEvaluationVo;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.mapper.LifeMessageMapper;
+import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.mapper.LifeUserMapper;
+import shop.alien.mapper.StoreDictionaryMapper;
+import shop.alien.mapper.second.*;
+import shop.alien.second.feign.AlienStoreFeign;
+import shop.alien.second.service.SecondTradeRecordService;
+import shop.alien.util.common.JwtUtil;
+
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 二手交易记录表 服务实现类
+ * </p>
+ *
+ * @author qrs
+ * @since 2025-07-07
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordMapper, SecondTradeRecord> implements SecondTradeRecordService {
+
+    private final SecondTradeRecordMapper secondTradeRecordMapper;
+    private final SecondTradeOperationMapper secondTradeOperationMapper;
+    private final SecondGoodsMapper secondGoodsMapper;
+    private final SecondGoodsRecordMapper secondGoodsRecordMapper;
+    private final SecondRiskControlRecordMapper secondRiskControlRecordMapper;
+    private final SecondUserCreditMapper secondUserCreditMapper;
+    private final SecondUserCreditRecordMapper secondUserCreditRecordMapper;
+    private final LifeMessageMapper lifeMessageMapper;
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final LifeUserMapper lifeUserMapper;
+    private final AlienStoreFeign alienStoreFeign;
+    private final StoreDictionaryMapper storeDictionaryMapper;
+    private final BaseRedisService baseRedisService;
+    @Autowired
+    private RiskControlProperties riskControlProperties;
+
+    // 委托人信息
+    private final SecondEntrustUserMapper secondEntrustUserMapper;
+
+    private final Object lock = new Object();
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean createTrade(SecondEntrustUserDTO trade) throws Exception {
+        try {
+            synchronized (lock) {
+                LocalDate now = LocalDate.now();
+                LambdaQueryWrapper<SecondTradeRecord> wrapper = new LambdaQueryWrapper<>();
+                wrapper.between(SecondTradeRecord::getCreatedTime, now + " 00:00:00", now + " 23:59:59");
+                int count = secondTradeRecordMapper.selectCount(wrapper);
+                String tradeNo = "S" + now.toString().replace("-", "") + String.format("%05d", count + 1);
+                trade.setTradeNo(tradeNo);
+            }
+            LambdaQueryWrapper<SecondGoodsRecord> goodsWrapper = new LambdaQueryWrapper<>();
+            goodsWrapper.eq(SecondGoodsRecord::getGoodsId, trade.getGoodsId());
+            goodsWrapper.eq(SecondGoodsRecord::getGoodsStatus, "3");
+            goodsWrapper.orderByDesc(SecondGoodsRecord::getCreatedTime);
+            goodsWrapper.last(" limit 1 ");
+            SecondGoodsRecord goodsRecord = secondGoodsRecordMapper.selectOne(goodsWrapper);
+            if (null != goodsRecord) trade.setGoodsRecordId(goodsRecord.getId());
+            // 保存交易记录
+            secondTradeRecordMapper.insert(trade);
+
+            // 保存交易操作表
+            SecondTradeOperation operation = new SecondTradeOperation();
+            operation.setTradeId(trade.getId());
+            operation.setUserId(Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"));
+            operation.setType(1);
+            operation.setCreatedTime(new Date());
+            secondTradeOperationMapper.insert(operation);
+
+            // 商品更新交易id
+            SecondGoods goods = new SecondGoods();
+            goods.setId(trade.getGoodsId());
+            goods.setTradeId(trade.getId());
+            secondGoodsMapper.updateById(goods);
+
+            // 查询商品
+            goods = secondGoodsMapper.selectById(trade.getGoodsId());
+
+            if (StringUtils.isNotBlank(trade.getEntrustUserPhone()) && StringUtils.isNotBlank(trade.getEntrustUserName()) && StringUtils.isNotBlank(trade.getEntrustIdCard())) {
+                // 添加商家委托信息
+                SecondEntrustUser secondEntrustUser = new SecondEntrustUser();
+                secondEntrustUser.setEntrustTradeId(trade.getId());
+                secondEntrustUser.setEntrustUserPhone(trade.getEntrustUserPhone());
+                secondEntrustUser.setEntrustUserName(trade.getEntrustUserName());
+                secondEntrustUser.setEntrustIdCard(trade.getEntrustIdCard());
+                secondEntrustUser.setEntrustTradeNo(trade.getTradeNo());
+                secondEntrustUser.setEntrustIdCardImg(trade.getEntrustIdCardImg());
+                secondEntrustUserMapper.insert(secondEntrustUser);
+            }
+
+            // 发送消息
+            sendMsg(goods, trade, 1, "4");
+
+            return true;
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.createTrade(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    /**
+     * 发送消息
+     * @param goods        商品信息
+     * @param trade        交易信息
+     * @param tradeStatus  1-待确认  2-已拒绝  3-待交易  6-交易取消
+     * @param messageType  4-二手交易创建/确认/拒绝/取消  5-二手交易签到提醒  6-二手交易已签到
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void sendMsg(SecondGoods goods, SecondTradeRecord trade, Integer tradeStatus, String messageType) {
+        // 封装交易信息
+        JSONObject message = new JSONObject();
+        message.put("title", goods.getTitle());
+        message.put("goodsId", goods.getId());
+        message.put("homeImage", goods.getHomeImage());
+        message.put("tradeId", trade.getId());
+        message.put("transactionAmount", trade.getTransactionAmount());
+        message.put("transactionLatitudeLongitude", trade.getTransactionLatitudeLongitude());
+        message.put("transactionLatitudeLongitudeAddress", trade.getTransactionLatitudeLongitudeAddress());
+        message.put("transactionLocation", trade.getTransactionLocation());
+        message.put("transactionTime", trade.getTransactionTime());
+        message.put("tradeStatus", tradeStatus);
+
+        // 查询商家委托信息
+        LambdaQueryWrapper<SecondEntrustUser> entrustWrapper = new LambdaQueryWrapper<>();
+        entrustWrapper.eq(SecondEntrustUser::getEntrustTradeId, trade.getId());
+        SecondEntrustUser entrustUser = secondEntrustUserMapper.selectOne(entrustWrapper);
+        if ( entrustUser != null ) {
+            message.put("entrustId", entrustUser.getId());
+            message.put("entrustUserPhone", entrustUser.getEntrustUserPhone());
+            message.put("entrustUserName", entrustUser.getEntrustUserName());
+            message.put("entrustIdCard", entrustUser.getEntrustIdCard());
+            message.put("entrustIdCardImg", entrustUser.getEntrustIdCardImg());
+        }
+
+        if (6 == tradeStatus) {
+            message.put("cancelUserId", trade.getCancelUserId());
+            message.put("cancelReason", trade.getCancelReason());
+            message.put("cancelReasonSupplement", trade.getCancelReasonSupplement());
+
+            // 查询字典表
+            LambdaQueryWrapper<StoreDictionary> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(StoreDictionary::getTypeName, "cancelTradeReason");
+            wrapper.eq(StoreDictionary::getDictId, trade.getCancelReason());
+            StoreDictionary storeDictionary = storeDictionaryMapper.selectOne(wrapper);
+            message.put("cancelReasonName", null == storeDictionary ? "" : storeDictionary.getDictDetail());
+        }
+
+        // 发起交易人信息
+        Integer userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+        String phoneId = JwtUtil.getCurrentUserInfo().getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");
+
+        // 获取交易对方信息
+        LifeUser lifeUser = lifeUserMapper.selectById(Objects.equals(userId, trade.getBuyerId()) ? trade.getSellerId() : trade.getBuyerId());
+        String receiverId = "user_" + lifeUser.getUserPhone();
+
+        // 消息
+        LifeMessage lifeMessage = new LifeMessage();
+        lifeMessage.setSenderId(phoneId);
+        lifeMessage.setReceiverId(receiverId);
+        lifeMessage.setType(messageType);
+        lifeMessage.setContent(message.toJSONString());
+        lifeMessageMapper.insert(lifeMessage);
+        message.put("createdTime", lifeMessage.getCreatedTime());
+
+        // 给买家与卖家发送交易消息
+        WebSocketVo webSocketVo = new WebSocketVo();
+        webSocketVo.setSenderId(phoneId);
+        webSocketVo.setReceiverId(receiverId);
+        webSocketVo.setCategory("message");
+        webSocketVo.setType(messageType);
+        webSocketVo.setIsRead(0);
+        webSocketVo.setText(message.toJSONString());
+        webSocketVo.setMessageId(lifeMessage.getId());
+
+        alienStoreFeign.sendMsgToClientByPhoneId(phoneId, JSONObject.from(webSocketVo).toJSONString());
+        alienStoreFeign.sendMsgToClientByPhoneId(receiverId, JSONObject.from(webSocketVo).toJSONString());
+    }
+
+    @Override
+    public boolean goodsTradeConfirm(int goodsId) throws Exception {
+        try {
+            LambdaQueryWrapper<SecondTradeRecord> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(SecondTradeRecord::getGoodsId, goodsId);
+            queryWrapper.in(SecondTradeRecord::getTradeStatus, 3, 4);
+            return secondTradeRecordMapper.selectCount(queryWrapper) == 0;
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.goodsTrade(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean tradeConfirm(int tradeId, int messageId, int type) throws BusinessException, Exception {
+        try {
+            // 交易信息
+            SecondTradeRecord trade = secondTradeRecordMapper.selectById(tradeId);
+
+            // 判断商品是否正在交易中
+            if (type == 1) {
+                LambdaQueryWrapper<SecondTradeRecord> recordWrapper = new LambdaQueryWrapper<>();
+                recordWrapper.eq(SecondTradeRecord::getGoodsId, trade.getGoodsId());
+                recordWrapper.eq(SecondTradeRecord::getTradeStatus, 3);
+                if (secondTradeRecordMapper.selectCount(recordWrapper) > 0) {
+                    throw new BusinessException("该商品正在交易中");
+                }
+            }
+
+            // 有定时任务: 超过交易时间还未确认的交易会自动取消并保存交易操作表  所以如果是已取消的交易  不需要进行这两步操作
+            if (trade.getTradeStatus() != 6) {
+                // 修改交易状态
+                SecondTradeRecord tradeRecord = new SecondTradeRecord();
+                tradeRecord.setId(tradeId);
+                tradeRecord.setTradeStatus(type == 1 ? 3 : 2);
+                secondTradeRecordMapper.updateById(tradeRecord);
+
+                // 保存交易操作表
+                SecondTradeOperation operation = new SecondTradeOperation();
+                operation.setTradeId(trade.getId());
+                operation.setUserId(Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"));
+                operation.setType(type == 1 ? 3 : 2);
+                secondTradeOperationMapper.insert(operation);
+            }
+
+            // 商品信息
+            SecondGoods goods = secondGoodsMapper.selectById(trade.getGoodsId());
+
+            // 修改待确认的消息
+            LifeMessage lastMessage = lifeMessageMapper.selectById(messageId);
+
+            JSONObject lastMessageContent = JSONObject.parseObject(lastMessage.getContent());
+            lastMessageContent.put("messageId", lastMessage.getId());
+            lastMessageContent.put("tradeStatus", trade.getTradeStatus() == 6 ? 6 : (type == 1 ? 3 : 2));
+            LifeMessage message = new LifeMessage();
+            message.setId(lastMessage.getId());
+            message.setContent(lastMessageContent.toJSONString());
+            lifeMessageMapper.updateById(message);
+
+            // 如果交易已取消  不需要发送消息
+            if (trade.getTradeStatus() != 6) {
+                // 发送消息
+                trade.setTradeStatus(type == 1 ? 3 : 2);
+                sendMsg(goods, trade, trade.getTradeStatus(), "4");
+            }
+
+            // 发起交易人信息
+            Integer userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            String phoneId = JwtUtil.getCurrentUserInfo().getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");
+
+            // 获取交易对方信息
+            LifeUser lifeUser = lifeUserMapper.selectById(Objects.equals(userId, trade.getBuyerId()) ? trade.getSellerId() : trade.getBuyerId());
+            String receiverId = "user_" + lifeUser.getUserPhone();
+
+            // 给买家与卖家发送交易消息
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId(phoneId);
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("message");
+            webSocketVo.setType("4");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(lastMessageContent.toJSONString());
+            webSocketVo.setMessageId(lastMessage.getId());
+
+            alienStoreFeign.sendMsgToClientByPhoneId(phoneId, JSONObject.from(webSocketVo).toJSONString());
+            alienStoreFeign.sendMsgToClientByPhoneId(receiverId, JSONObject.from(webSocketVo).toJSONString());
+
+            // 交易已取消
+            if (trade.getTradeStatus() == 6 ) {
+                throw new BusinessException("该交易已取消,不可操作");
+            }
+
+            // 创建交易时间小于十分钟,直接发送交易提醒
+            if (1 == type && Math.abs(Duration.between(Instant.now(), trade.getTransactionTime().toInstant()).getSeconds()) < 600) {
+                sendSignInMessage(trade);
+            }
+
+            // 风控机制 - 高频高价交易
+            LambdaQueryWrapper<SecondTradeRecord> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(SecondTradeRecord::getSellerId, trade.getSellerId());
+            wrapper.in(SecondTradeRecord::getTradeStatus, "3, 4");
+            wrapper.between(SecondTradeRecord::getTransactionTime,
+                    trade.getTransactionTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate() + " 00:00:00",
+                    trade.getTransactionTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate() + " 23:59:59");
+            wrapper.gt(SecondTradeRecord::getTransactionAmount, riskControlProperties.getMoneyLaundering().getAmountThreshold());
+            List<SecondTradeRecord> recordList = secondTradeRecordMapper.selectList(wrapper);
+            if (recordList.size() > riskControlProperties.getMoneyLaundering().getDailyCount()) {
+                String startDate = LocalDateTime.now().minusHours(24L).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+                String endDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+                List<SecondUserCreditRecord> num = secondUserCreditRecordMapper.selectList(new LambdaQueryWrapper<SecondUserCreditRecord>()
+                        .eq(SecondUserCreditRecord::getUserId, trade.getSellerId())
+                        .eq(SecondUserCreditRecord::getPointsType, SecondUserCreditScoreEnum.SUSPECTED_OF_MONEY_LAUNDERING.getCode())
+                        .ge(SecondUserCreditRecord::getCreatedTime, startDate)
+                        .le(SecondUserCreditRecord::getCreatedTime, endDate));
+                if (num.size() <= 0) {
+                    LambdaQueryWrapper<SecondUserCredit> queryWrapper = new LambdaQueryWrapper<>();
+                    queryWrapper.eq(SecondUserCredit::getUserId, trade.getSellerId())
+                            .orderByDesc(SecondUserCredit::getCreatedTime).last("LIMIT 1");
+                    SecondUserCredit secondUserCredit = secondUserCreditMapper.selectOne(queryWrapper);
+                    int score = secondUserCredit.getUserPoints() + SecondUserCreditScoreEnum.SUSPECTED_OF_MONEY_LAUNDERING.getScore();
+                    secondUserCredit.setUserPoints(score);
+                    secondUserCreditMapper.updateById(secondUserCredit);
+
+                    SecondUserCreditRecord record = new SecondUserCreditRecord();
+                    record.setUserId(trade.getSellerId());
+                    record.setPointsType(SecondUserCreditScoreEnum.SUSPECTED_OF_MONEY_LAUNDERING.getCode());
+                    record.setPoints(SecondUserCreditScoreEnum.SUSPECTED_OF_MONEY_LAUNDERING.getScore());
+                    record.setCurrentScoreCount(score);
+                    record.setCreatedTime(new Date());
+                    secondUserCreditRecordMapper.insert(record);
+
+                    // 小于100分封禁用户
+                    if (score < 100) {
+                        LifeUser user = new LifeUser();
+                        user.setId(trade.getSellerId());
+                        user.setIsBanned(1);
+                        lifeUserMapper.updateById(user);
+                    }
+                }
+
+//                SecondRiskControlRecord riskControlRecord = new SecondRiskControlRecord();
+//                riskControlRecord.setRuleType(1);
+//                riskControlRecord.setUserId(trade.getSellerId());
+//                riskControlRecord.setBusinessId(String.valueOf(trade.getSellerId()));
+//                riskControlRecord.setRuleName(riskControlProperties.getMoneyLaundering().getRiskType());
+//                JSONArray detailInfo = new JSONArray();
+//                detailInfo.addAll(recordList.stream().map(SecondTradeRecord::getId).collect(Collectors.toList()));
+//                riskControlRecord.setDetailInfo(detailInfo.toJSONString());
+//                secondRiskControlRecordMapper.insert(riskControlRecord);
+            }
+
+            return true;
+        } catch (BusinessException e) {
+            throw new BusinessException(e);
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.tradeConfirm(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean cancelTrade(int tradeId, String cancelReason, String cancelReasonSupplement) throws Exception {
+        try {
+            // 发起交易人信息
+            Integer userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+
+            // 修改交易状态
+            SecondTradeRecord tradeRecord = new SecondTradeRecord();
+            tradeRecord.setId(tradeId);
+            tradeRecord.setTradeStatus(6);
+            tradeRecord.setCancelUserId(userId);
+            tradeRecord.setCancelReason(cancelReason);
+            tradeRecord.setCancelReasonSupplement(cancelReasonSupplement);
+            secondTradeRecordMapper.updateById(tradeRecord);
+
+            // 保存交易操作表
+            SecondTradeOperation operation = new SecondTradeOperation();
+            operation.setTradeId(tradeId);
+            operation.setUserId(Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"));
+            operation.setType(7);
+            operation.setCreatedTime(new Date());
+            secondTradeOperationMapper.insert(operation);
+
+            // 交易信息
+            SecondTradeRecord trade = secondTradeRecordMapper.selectById(tradeId);
+
+            // 发送消息
+            sendMsg(secondGoodsMapper.selectById(trade.getGoodsId()), trade, 6, "4");
+
+            return true;
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.cancelTrade(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public boolean whetherCanSignIn(int tradeId) throws Exception {
+        try {
+            int userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            SecondTradeRecord trade = secondTradeRecordMapper.selectById(tradeId);
+            if (null == trade) return false;
+
+            if (userId == trade.getBuyerId() && 0 == trade.getBuyerSignIn()) return true;
+            if (userId == trade.getSellerId() && 0 == trade.getSellerSignIn()) return true;
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.whetherCanSignIn(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+        return false;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean tradeSignIn(int tradeId, int messageId, String signInLatitudeLongitude, String signInLatitudeLongitudeAddress) throws BusinessException, Exception {
+        try {
+            SecondTradeRecord trade = secondTradeRecordMapper.selectById(tradeId);
+            if (null == trade) return false;
+            if (trade.getTradeStatus() != 3) return false;
+
+            // 发起交易人信息
+            int userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            String phoneId = JwtUtil.getCurrentUserInfo().getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");
+
+            // 获取交易对方信息
+            LifeUser lifeUser = lifeUserMapper.selectById(Objects.equals(userId, trade.getBuyerId()) ? trade.getSellerId() : trade.getBuyerId());
+            String receiverId = "user_" + lifeUser.getUserPhone();
+
+            // 保存交易签到信息
+            SecondTradeRecord record = new SecondTradeRecord();
+            record.setId(tradeId);
+            Date now = Date.from(LocalDateTime.now().withSecond(0).withNano(0).atZone(ZoneId.systemDefault()).toInstant());
+            if (userId == trade.getBuyerId()) {
+                if (1 == trade.getBuyerSignIn()) throw new BusinessException("该交易已签到,无需再次签到");
+                record.setBuyerSignIn(1);
+                record.setBuyerSignInTime(now);
+                record.setBuyerSignInLatitudeLongitude(signInLatitudeLongitude);
+                record.setBuyerSignInLatitudeLongitudeAddress(signInLatitudeLongitudeAddress);
+            } else if (userId == trade.getSellerId()) {
+                if (1 == trade.getSellerSignIn()) throw new BusinessException("该交易已签到,无需再次签到");
+                record.setSellerSignIn(1);
+                record.setSellerSignInTime(now);
+                record.setSellerSignInLatitudeLongitude(signInLatitudeLongitude);
+                record.setSellerSignInLatitudeLongitudeAddress(signInLatitudeLongitudeAddress);
+            } else {
+                log.error("SecondTradeRecordServiceImpl.tradeSignIn(): Error Msg={}", "userId无效");
+                throw new Exception();
+            }
+            secondTradeRecordMapper.updateById(record);
+
+            // 保存交易操作表
+            SecondTradeOperation operation = new SecondTradeOperation();
+            operation.setTradeId(tradeId);
+            operation.setUserId(userId);
+            operation.setType(4);
+            operation.setCreatedTime(new Date());
+            secondTradeOperationMapper.insert(operation);
+
+            // 删除签到提醒消息
+            LifeMessage signInMessage = lifeMessageMapper.selectById(messageId);
+            if (null == signInMessage) throw new BusinessException("该交易已签到,无需再次签到");
+            lifeMessageMapper.deleteById(messageId);
+
+            // 发送已签到消息
+            JSONObject messageContent = JSONObject.parseObject(signInMessage.getContent());
+            messageContent.put("tradeId", tradeId);
+            messageContent.put("signInTime", now.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
+            messageContent.put("signInLatitudeLongitude", signInLatitudeLongitude);
+            messageContent.put("signInLatitudeLongitudeAddress", signInLatitudeLongitudeAddress);
+            LifeMessage message = new LifeMessage();
+            message.setSenderId(phoneId);
+            message.setReceiverId(receiverId);
+            message.setContent(messageContent.toJSONString());
+            message.setType("6");
+            lifeMessageMapper.insert(message);
+
+            // 推送已签到消息
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId(phoneId);
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("message");
+            webSocketVo.setType("6");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(messageContent.toJSONString());
+            webSocketVo.setMessageId(message.getId());
+
+            alienStoreFeign.sendMsgToClientByPhoneId(phoneId, JSONObject.from(webSocketVo).toJSONString());
+            alienStoreFeign.sendMsgToClientByPhoneId(receiverId, JSONObject.from(webSocketVo).toJSONString());
+
+            return true;
+        } catch (BusinessException e) {
+            throw new BusinessException(e);
+        }  catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.tradeSignIn(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public SecondTradeRecordVo getUserTradeStatus(int tradeId) throws Exception {
+        try {
+            SecondTradeRecordVo recordVo = new SecondTradeRecordVo();
+            int userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            SecondTradeRecord tradeRecord = secondTradeRecordMapper.selectById(tradeId);
+            if (null == tradeRecord) return new SecondTradeRecordVo();
+
+            if (userId == tradeRecord.getBuyerId()) {
+                recordVo.setUserTransactionStatus(tradeRecord.getBuyerTransactionStatus());
+                recordVo.setUserEvaluate(tradeRecord.getBuyerEvaluate());
+                recordVo.setUserRating(tradeRecord.getBuyerRating());
+            } else if (userId == tradeRecord.getSellerId()) {
+                recordVo.setUserTransactionStatus(tradeRecord.getSellerTransactionStatus());
+                recordVo.setUserEvaluate(tradeRecord.getSellerEvaluate());
+                recordVo.setUserRating(tradeRecord.getSellerRating());
+            }
+            recordVo.setSellerId(tradeRecord.getSellerId());
+            return recordVo;
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.getUserTradeStatus(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean tradeCompleteConfirm(int tradeId, int type, String evaluate, Integer rating) throws Exception {
+        try {
+            int userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            SecondTradeRecord tradeRecord = secondTradeRecordMapper.selectById(tradeId);
+            SecondTradeRecord record = new SecondTradeRecord();
+            record.setId(tradeId);
+            // 买家
+            if (userId == tradeRecord.getBuyerId()) {
+                if (0 != tradeRecord.getBuyerTransactionStatus()) return false;
+                record.setBuyerTransactionStatus(type);
+                record.setBuyerCompleteTime(new Date());
+                record.setBuyerEvaluate(evaluate);
+                record.setBuyerRating(rating);
+            // 卖家
+            } else if (userId == tradeRecord.getSellerId()) {
+                if (0 != tradeRecord.getSellerTransactionStatus()) return false;
+                record.setSellerTransactionStatus(type);
+                record.setSellerCompleteTime(new Date());
+                record.setSellerEvaluate(evaluate);
+                record.setSellerRating(rating);
+                record.setTradeStatus(1 == type ? 4 : 5);
+                // 卖家如果选择交易成功  同步修改商品表
+                if (1 == type) {
+                    SecondGoods goods = new SecondGoods();
+                    goods.setId(tradeRecord.getGoodsId());
+                    goods.setGoodsStatus(5);
+                    secondGoodsMapper.updateById(goods);
+                }
+            } else {
+                return false;
+            }
+            secondTradeRecordMapper.updateById(record);
+
+            // 保存交易操作表
+            SecondTradeOperation operation = new SecondTradeOperation();
+            operation.setTradeId(tradeId);
+            operation.setUserId(userId);
+            operation.setType(1 == type ? 5 : 6);
+            operation.setCreatedTime(new Date());
+            secondTradeOperationMapper.insert(operation);
+
+            // 添加信用分
+            if (1 == type && userId == tradeRecord.getSellerId()) {
+                creditScore(tradeRecord.getBuyerId());
+                creditScore(tradeRecord.getSellerId());
+            }
+
+            return true;
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.tradeCompleteConfirm(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    /**
+     * 增加交易信用分
+     * @param userId 用户id
+     */
+    private void creditScore(Integer userId) {
+        LambdaQueryWrapper<SecondUserCreditRecord> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SecondUserCreditRecord::getUserId, userId);
+        wrapper.eq(SecondUserCreditRecord::getPointsType, 3);
+        List<SecondUserCreditRecord> records = secondUserCreditRecordMapper.selectList(wrapper);
+        int userPoints = records.stream().mapToInt(SecondUserCreditRecord::getPoints).sum();
+        if (userPoints < 500) {
+            SecondUserCreditRecord record = new SecondUserCreditRecord();
+            record.setUserId(userId);
+            record.setPoints(50);
+            record.setPointsType(3);
+            secondUserCreditRecordMapper.insert(record);
+            secondUserCreditMapper.updatePointsByUserId(userId, 50);
+        }
+    }
+
+    @Override
+    public List<SecondTradeRecordVo> getTradeRecord(int sideId) throws Exception {
+        try {
+            int userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            QueryWrapper<SecondTradeRecord> wrapper = new QueryWrapper<>();
+            wrapper.eq("trade.delete_flag", 0);
+            wrapper.apply("((trade.buyer_id = '" + sideId + "' and trade.seller_id = '" + userId + "') || (trade.buyer_id = '" + userId + "' and trade.seller_id = '" + sideId + "'))");
+            wrapper.orderByDesc("trade.created_time");
+            List<SecondTradeRecordVo> vo = secondTradeRecordMapper.getTradeRecord(wrapper);
+            if (vo.size() > 0) {
+                for (SecondTradeRecordVo item : vo) {
+                    // 查询商家委托信息
+                    LambdaQueryWrapper<SecondEntrustUser> entrustWrapper = new LambdaQueryWrapper<>();
+                    entrustWrapper.eq(SecondEntrustUser::getEntrustTradeId, item.getId());
+                    SecondEntrustUser entrustUser = secondEntrustUserMapper.selectOne(entrustWrapper);
+                    if ( entrustUser != null ) {
+                        item.setEntrustId(entrustUser.getId());
+                        item.setEntrustUserPhone(entrustUser.getEntrustUserPhone());
+                        item.setEntrustUserName(entrustUser.getEntrustUserName());
+                        item.setEntrustIdCard(entrustUser.getEntrustIdCard());
+                        item.setEntrustIdCardImg(entrustUser.getEntrustIdCardImg());
+                    }
+                }
+            }
+            return vo;
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.getTradeRecord(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public SecondTradeRecord hasInTradeRecord(Integer sideId) throws Exception {
+        try {
+            int userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            LambdaQueryWrapper<SecondTradeRecord> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(SecondTradeRecord::getTradeStatus, 3);
+            wrapper.apply("((seller_id = " + userId + " and buyer_id = " + sideId + ") or " +
+                    "(seller_id = " + sideId + " and buyer_id = " + userId + "))");
+            wrapper.orderByAsc(SecondTradeRecord::getTransactionTime);
+            List<SecondTradeRecord> list = secondTradeRecordMapper.selectList(wrapper);
+            if (list.isEmpty()) return null;
+
+            // 取出所有的交易时间
+            List<Date> dates = list.stream().map(SecondTradeRecord::getTransactionTime).collect(Collectors.toList());
+            if (dates.isEmpty()) return null;
+
+            // 取出距离当前时间最近的交易时间
+            Date now = new Date();
+            Date minDate = dates.stream()
+                    .min(Comparator.comparingLong(date ->
+                            Math.abs(date.getTime() - now.getTime())))
+                    .orElse(null);
+
+            list = list.stream().filter(item -> item.getTransactionTime().equals(minDate)).collect(Collectors.toList());
+            return list.isEmpty() ? null : (1 == list.get(0).getBuyerSignIn() && 1 == list.get(0).getSellerSignIn() ? null : list.get(0));
+         } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.hasInTradeRecord(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean modifyTradeRecord(int type, Integer tradeId, String transactionTime, String transactionLatitudeLongitude, String transactionLatitudeLongitudeAddress, String transactionLocation, String messageId,
+                                     String userPhone, String userName, String idCard, Integer entrustId, String idCardImg) throws Exception {
+        try {
+            if (type == 1) {
+                LambdaUpdateWrapper<SecondTradeRecord> wrapper = new LambdaUpdateWrapper<>();
+                wrapper.set(SecondTradeRecord::getTransactionTime, transactionTime);
+                wrapper.set(SecondTradeRecord::getTransactionLatitudeLongitude, transactionLatitudeLongitude);
+                wrapper.set(SecondTradeRecord::getTransactionLatitudeLongitudeAddress, transactionLatitudeLongitudeAddress);
+                wrapper.set(SecondTradeRecord::getTransactionLocation, transactionLocation);
+                // 清空签到信息
+                wrapper.set(SecondTradeRecord::getBuyerSignIn, 0);
+                wrapper.set(SecondTradeRecord::getBuyerSignInTime, null);
+                wrapper.set(SecondTradeRecord::getBuyerSignInLatitudeLongitude, null);
+                wrapper.set(SecondTradeRecord::getBuyerSignInLatitudeLongitudeAddress, null);
+                wrapper.set(SecondTradeRecord::getSellerSignIn, 0);
+                wrapper.set(SecondTradeRecord::getSellerSignInTime, null);
+                wrapper.set(SecondTradeRecord::getSellerSignInLatitudeLongitude, null);
+                wrapper.set(SecondTradeRecord::getSellerSignInLatitudeLongitudeAddress, null);
+                wrapper.eq(SecondTradeRecord::getId, tradeId);
+                secondTradeRecordMapper.update(null, wrapper);
+            }
+            // 修改待确认的消息
+            LifeMessage lastMessage = lifeMessageMapper.selectById(messageId);
+
+            JSONObject lastMessageContent = JSONObject.parseObject(lastMessage.getContent());
+            lastMessageContent.put("messageId", lastMessage.getId());
+            lastMessageContent.put("modifyStatus", type == 1 ? 3 : 2);
+            LifeMessage message = new LifeMessage();
+            message.setId(lastMessage.getId());
+            message.setContent(lastMessageContent.toJSONString());
+            lifeMessageMapper.updateById(message);
+
+            SecondTradeRecord trade = secondTradeRecordMapper.selectById(tradeId);
+
+            // 发起交易人信息
+            Integer userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            String phoneId = JwtUtil.getCurrentUserInfo().getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");
+
+            // 获取交易对方信息
+            LifeUser lifeUser = lifeUserMapper.selectById(Objects.equals(userId, trade.getBuyerId()) ? trade.getSellerId() : trade.getBuyerId());
+            String receiverId = "user_" + lifeUser.getUserPhone();
+
+            if (entrustId != null) {
+                LambdaUpdateWrapper<SecondEntrustUser> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(SecondEntrustUser::getId, entrustId);
+                if (StringUtils.isBlank(userPhone) && StringUtils.isBlank(userName) && StringUtils.isBlank(idCard)) {
+                    updateWrapper.set(SecondEntrustUser::getDeleteFlag, 1);
+                } else {
+                    updateWrapper.set(SecondEntrustUser::getEntrustTradeId, tradeId);
+                    updateWrapper.set(SecondEntrustUser::getEntrustUserPhone, userPhone);
+                    updateWrapper.set(SecondEntrustUser::getEntrustUserName, userName);
+                    updateWrapper.set(SecondEntrustUser::getEntrustIdCard, idCard);
+                    updateWrapper.set(SecondEntrustUser::getEntrustIdCardImg, idCardImg);
+                }
+                secondEntrustUserMapper.update(null, updateWrapper);
+            } else {
+                LambdaUpdateWrapper<SecondEntrustUser> wrapper = new LambdaUpdateWrapper<>();
+                wrapper.eq(SecondEntrustUser::getEntrustTradeId, tradeId);
+                Integer entrust = secondEntrustUserMapper.selectCount(wrapper);
+                if (entrust <= 0) {
+                    if (StringUtils.isNotBlank(userPhone) && StringUtils.isNotBlank(userName) && StringUtils.isNotBlank(idCard)) {
+                        secondEntrustUserMapper.insert(new SecondEntrustUser()
+                                .setEntrustTradeId(tradeId)
+                                .setEntrustUserPhone(userPhone)
+                                .setEntrustUserName(userName)
+                                .setEntrustIdCard(idCard)
+                                .setEntrustIdCardImg(idCardImg)
+                                .setDeleteFlag(0));
+                    }
+                }
+            }
+
+
+            // 给买家与卖家发送交易消息
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId(phoneId);
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("message");
+            webSocketVo.setType("9");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(lastMessageContent.toJSONString());
+            webSocketVo.setMessageId(lastMessage.getId());
+
+            alienStoreFeign.sendMsgToClientByPhoneId(phoneId, JSONObject.from(webSocketVo).toJSONString());
+            alienStoreFeign.sendMsgToClientByPhoneId(receiverId, JSONObject.from(webSocketVo).toJSONString());
+
+            // 交易时间小于十分钟,直接发送交易提醒
+            if (1 == type && Math.abs(Duration.between(Instant.now(), trade.getTransactionTime().toInstant()).getSeconds()) < 600) {
+                sendSignInMessage(trade);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.modifyTradeRecord(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    private void sendSignInMessage(SecondTradeRecord tradeRecord) throws Exception {
+        log.info("二手交易平台-创建交易时间小于十分钟,直接发送交易提醒---sendSignInMessage()---");
+        try {
+            LifeUser buyer = lifeUserMapper.selectById(tradeRecord.getBuyerId());
+            LifeUser seller = lifeUserMapper.selectById(tradeRecord.getSellerId());
+
+            String buyerPhoneId = null == buyer ? "" : "user_" + buyer.getUserPhone();
+            String sellerPhoneId = null == seller ? "" : "user_" + seller.getUserPhone();
+
+            // 封装交易信息
+            JSONObject message = new JSONObject();
+            message.put("tradeId", tradeRecord.getId());
+            message.put("transactionAmount", tradeRecord.getTransactionAmount());
+            message.put("transactionLatitudeLongitude", tradeRecord.getTransactionLatitudeLongitude());
+            message.put("transactionLatitudeLongitudeAddress", tradeRecord.getTransactionLatitudeLongitudeAddress());
+            message.put("transactionLocation", tradeRecord.getTransactionLocation());
+            message.put("transactionTime", tradeRecord.getTransactionTime());
+            message.put("tradeStatus", tradeRecord.getTradeStatus());
+
+            // 给买家发送消息
+            LifeMessage lifeMessage = new LifeMessage();
+            lifeMessage.setSenderId(sellerPhoneId);
+            lifeMessage.setReceiverId(buyerPhoneId);
+            lifeMessage.setContent(message.toJSONString());
+            lifeMessage.setType("5");
+            lifeMessageMapper.insert(lifeMessage);
+
+            // 给买家推送消息
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId(sellerPhoneId);
+            webSocketVo.setReceiverId(buyerPhoneId);
+            webSocketVo.setCategory("message");
+            webSocketVo.setType("5");
+            webSocketVo.setText(message.toJSONString());
+            webSocketVo.setMessageId(lifeMessage.getId());
+            alienStoreFeign.sendMsgToClientByPhoneId(buyerPhoneId, JSONObject.from(webSocketVo).toJSONString());
+
+            // 给买家发送通知
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setSenderId("system");
+            lifeNotice.setReceiverId(buyerPhoneId);
+            lifeNotice.setBusinessId(tradeRecord.getId());
+            lifeNotice.setTitle("商品交易签到");
+            lifeNotice.setNoticeType(1);
+            // 封装通知信息
+            JSONObject noticeMessage = new JSONObject();
+            noticeMessage.put("tradeId", tradeRecord.getId());
+            noticeMessage.put("otherSideUserId", tradeRecord.getSellerId());
+            noticeMessage.put("otherSidePhoneId", sellerPhoneId);
+            noticeMessage.put("otherSideName", null == seller ? "" : seller.getUserName());
+            noticeMessage.put("otherSideImage", null == seller ? "" : seller.getUserImage());
+            noticeMessage.put("message", "您有一笔交易即将开始, 请及时前往查看");
+            lifeNotice.setContext(noticeMessage.toJSONString());
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 给买家推送通知
+            webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId("system");
+            webSocketVo.setReceiverId(buyerPhoneId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setType("5");
+            webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+            webSocketVo.setMessageId(lifeMessage.getId());
+            alienStoreFeign.sendMsgToClientByPhoneId(buyerPhoneId, JSONObject.from(webSocketVo).toJSONString());
+
+            // 给卖家发送消息
+            lifeMessage = new LifeMessage();
+            lifeMessage.setSenderId(buyerPhoneId);
+            lifeMessage.setReceiverId(sellerPhoneId);
+            lifeMessage.setContent(message.toJSONString());
+            lifeMessage.setType("5");
+            lifeMessageMapper.insert(lifeMessage);
+
+            // 给卖家推送消息
+            webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId(buyerPhoneId);
+            webSocketVo.setReceiverId(sellerPhoneId);
+            webSocketVo.setCategory("message");
+            webSocketVo.setType("5");
+            webSocketVo.setText(message.toJSONString());
+            webSocketVo.setMessageId(lifeMessage.getId());
+            alienStoreFeign.sendMsgToClientByPhoneId(sellerPhoneId, JSONObject.from(webSocketVo).toJSONString());
+
+            // 给卖家发送通知
+            lifeNotice = new LifeNotice();
+            lifeNotice.setSenderId("system");
+            lifeNotice.setReceiverId(sellerPhoneId);
+            lifeNotice.setBusinessId(tradeRecord.getId());
+            lifeNotice.setTitle("商品交易签到");
+            lifeNotice.setNoticeType(1);
+            // 封装通知信息
+            noticeMessage = new JSONObject();
+            noticeMessage.put("tradeId", tradeRecord.getId());
+            noticeMessage.put("otherSideUserId", tradeRecord.getBuyerId());
+            noticeMessage.put("otherSidePhoneId", buyerPhoneId);
+            noticeMessage.put("otherSideName", null == buyer ? "" : buyer.getUserName());
+            noticeMessage.put("otherSideImage", null == buyer ? "" : buyer.getUserImage());
+            noticeMessage.put("message", "您有一笔交易即将开始, 请及时前往查看");
+            lifeNotice.setContext(noticeMessage.toJSONString());
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 给卖家推送通知
+            webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId("system");
+            webSocketVo.setReceiverId(sellerPhoneId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setType("5");
+            webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+            webSocketVo.setMessageId(lifeMessage.getId());
+            alienStoreFeign.sendMsgToClientByPhoneId(sellerPhoneId, JSONObject.from(webSocketVo).toJSONString());
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.sendSignInMessage(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    /**
+     * 获取用户作为卖家的交易评价列表(分页)
+     *
+     * @param sellerId 卖家ID(用户ID,必传)
+     * @param pageNum 页码
+     * @param pageSize 每页条数
+     * @return 卖家评价分页列表
+     */
+    @Override
+    public IPage<SellerEvaluationVo> getSellerEvaluationList(Integer sellerId, Integer pageNum, Integer pageSize) throws Exception {
+        try {
+            log.info("SecondTradeRecordServiceImpl.getSellerEvaluationList(): sellerId={}, pageNum={}, pageSize={}", 
+                    sellerId, pageNum, pageSize);
+            
+            // sellerId必传校验
+            if (sellerId == null) {
+                throw new Exception("卖家ID不能为空");
+            }
+            
+            // 参数默认值处理
+            if (pageNum == null || pageNum < 1) {
+                pageNum = 1;
+            }
+            if (pageSize == null || pageSize < 1) {
+                pageSize = 10;
+            }
+            // 限制最大每页条数
+            if (pageSize > 100) {
+                pageSize = 100;
+            }
+            
+            // 使用MyBatis Plus分页查询交易记录
+            Page<SecondTradeRecord> page = new Page<>(pageNum, pageSize);
+            LambdaQueryWrapper<SecondTradeRecord> tradeWrapper = new LambdaQueryWrapper<>();
+            tradeWrapper.eq(SecondTradeRecord::getSellerId, sellerId)
+                    .eq(SecondTradeRecord::getDeleteFlag, 0)
+                    .isNotNull(SecondTradeRecord::getBuyerEvaluate)
+                    .ne(SecondTradeRecord::getBuyerEvaluate, "")
+                    .orderByDesc(SecondTradeRecord::getCreatedTime);
+            
+            IPage<SecondTradeRecord> tradePage = secondTradeRecordMapper.selectPage(page, tradeWrapper);
+            List<SecondTradeRecord> tradeRecords = tradePage.getRecords();
+            
+            // 如果没有数据,直接返回空分页对象
+            if (tradeRecords.isEmpty()) {
+                log.info("SecondTradeRecordServiceImpl.getSellerEvaluationList(): 未查询到评价记录");
+                Page<SellerEvaluationVo> emptyPage = new Page<>(pageNum, pageSize);
+                emptyPage.setTotal(0);
+                return emptyPage;
+            }
+            
+            // 获取所有商品记录ID、买家ID和卖家ID
+            List<Integer> goodsRecordIds = tradeRecords.stream()
+                    .map(SecondTradeRecord::getGoodsRecordId)
+                    .filter(Objects::nonNull)
+                    .distinct()
+                    .collect(Collectors.toList());
+            
+            List<Integer> buyerIds = tradeRecords.stream()
+                    .map(SecondTradeRecord::getBuyerId)
+                    .filter(Objects::nonNull)
+                    .distinct()
+                    .collect(Collectors.toList());
+            
+            // 批量查询商品记录信息
+            Map<Integer, SecondGoodsRecord> goodsRecordMap = new HashMap<>();
+            if (!goodsRecordIds.isEmpty()) {
+                LambdaQueryWrapper<SecondGoodsRecord> goodsWrapper = new LambdaQueryWrapper<>();
+                goodsWrapper.in(SecondGoodsRecord::getId, goodsRecordIds);
+                List<SecondGoodsRecord> goodsRecords = secondGoodsRecordMapper.selectList(goodsWrapper);
+                goodsRecordMap = goodsRecords.stream()
+                        .collect(Collectors.toMap(SecondGoodsRecord::getId, g -> g));
+            }
+            
+            // 批量查询买家用户信息
+            Map<Integer, LifeUser> buyerMap = new HashMap<>();
+            if (!buyerIds.isEmpty()) {
+                LambdaQueryWrapper<LifeUser> userWrapper = new LambdaQueryWrapper<>();
+                userWrapper.in(LifeUser::getId, buyerIds)
+                        .eq(LifeUser::getDeleteFlag, 0);
+                List<LifeUser> buyers = lifeUserMapper.selectList(userWrapper);
+                buyerMap = buyers.stream()
+                        .collect(Collectors.toMap(LifeUser::getId, u -> u));
+            }
+            
+            // 查询卖家信息
+            LifeUser seller = lifeUserMapper.selectById(sellerId);
+            
+            // 批量查询买家和卖家的信用积分
+            List<Integer> allUserIds = new ArrayList<>(buyerIds);
+            allUserIds.add(sellerId);
+            Map<Integer, SecondUserCredit> creditMap = new HashMap<>();
+            if (!allUserIds.isEmpty()) {
+                LambdaQueryWrapper<SecondUserCredit> creditWrapper = new LambdaQueryWrapper<>();
+                creditWrapper.in(SecondUserCredit::getUserId, allUserIds)
+                        .eq(SecondUserCredit::getDeleteFlag, 0);
+                List<SecondUserCredit> credits = secondUserCreditMapper.selectList(creditWrapper);
+                creditMap = credits.stream()
+                        .collect(Collectors.toMap(SecondUserCredit::getUserId, c -> c));
+            }
+            
+            // 组装返回结果
+            List<SellerEvaluationVo> evaluationList = new ArrayList<>();
+            for (SecondTradeRecord trade : tradeRecords) {
+                SellerEvaluationVo vo = new SellerEvaluationVo();
+                
+                // 交易基本信息
+                vo.setId(trade.getId());
+                vo.setTradeNo(trade.getTradeNo());
+                vo.setGoodsId(trade.getGoodsId());
+                vo.setTransactionAmount(trade.getTransactionAmount());
+                vo.setBuyerId(trade.getBuyerId());
+                vo.setBuyerEvaluate(trade.getBuyerEvaluate());
+                // 卖家交易完成时间(评价时间,评分时间)
+                vo.setSellerCompleteTime(trade.getSellerCompleteTime());
+                // 买家交易完成时间(评价时间,评分时间)
+                vo.setBuyerCompleteTime(trade.getBuyerCompleteTime());
+                vo.setBuyerRating(trade.getBuyerRating());
+                vo.setTransactionTime(trade.getTransactionTime());
+                vo.setTradeStatus(trade.getTradeStatus());
+                vo.setCreatedTime(trade.getCreatedTime());
+                
+                // 商品信息
+                SecondGoodsRecord goodsRecord = goodsRecordMap.get(trade.getGoodsRecordId());
+                if (goodsRecord != null) {
+                    vo.setGoodsTitle(goodsRecord.getTitle());
+                    vo.setGoodsHomeImage(goodsRecord.getHomeImage());
+                    vo.setGoodsPrice(goodsRecord.getPrice());
+                }
+                
+                // 买家用户信息
+                LifeUser buyer = buyerMap.get(trade.getBuyerId());
+                if (buyer != null) {
+                    vo.setBuyerName(buyer.getUserName());
+                    vo.setBuyerImage(buyer.getUserImage());
+                    vo.setBuyerPhone(buyer.getUserPhone());
+                    vo.setBuyerRealName(buyer.getRealName());
+                    vo.setBuyerSex(buyer.getUserSex());
+                    vo.setBuyerJianjie(buyer.getJianjie());
+                }
+                
+                // 买家信用积分和风控评分等级
+                SecondUserCredit buyerCredit = creditMap.get(trade.getBuyerId());
+                if (buyerCredit != null) {
+                    vo.setBuyerCreditScore(buyerCredit.getUserPoints());
+                } else {
+                    vo.setBuyerCreditScore(0);
+                }
+                vo.setBuyerCreditLevel(calculateCreditLevel(trade.getBuyerId()));
+                
+                // 卖家信息
+                vo.setSellerId(sellerId);
+                if (seller != null) {
+                    vo.setSellerName(seller.getUserName());
+                    vo.setSellerImage(seller.getUserImage());
+                }
+                
+                // 卖家信用积分和风控评分等级
+                SecondUserCredit sellerCredit = creditMap.get(sellerId);
+                if (sellerCredit != null) {
+                    vo.setSellerCreditScore(sellerCredit.getUserPoints());
+                } else {
+                    vo.setSellerCreditScore(0);
+                }
+                vo.setSellerCreditLevel(calculateCreditLevel(sellerId));
+                
+                evaluationList.add(vo);
+            }
+            
+            // 构建分页返回对象
+            Page<SellerEvaluationVo> resultPage = new Page<>(pageNum, pageSize);
+            resultPage.setRecords(evaluationList);
+            resultPage.setTotal(tradePage.getTotal());
+            resultPage.setPages(tradePage.getPages());
+            
+            log.info("SecondTradeRecordServiceImpl.getSellerEvaluationList(): 查询到{}条评价记录,共{}页", 
+                    evaluationList.size(), resultPage.getPages());
+            return resultPage;
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.getSellerEvaluationList(): Error Msg={}", e.getMessage(), e);
+            throw new Exception("获取卖家评价列表失败: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public void locationShareAdd(Integer otherUserId) throws Exception {
+        try {
+            Integer userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            String key = getKey(userId, otherUserId);
+
+            if (baseRedisService.hasKey(key)) {
+                JSONArray array = JSONArray.parseArray(baseRedisService.getString(key));
+                if (!array.contains(userId)) array.add(userId);
+                baseRedisService.setString(key, array.toJSONString());
+            } else {
+                JSONArray array = new JSONArray();
+                array.add(userId);
+                baseRedisService.setString(key, array.toJSONString());
+            }
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.locationShareAdd(): Error Msg={}", e.getMessage(), e);
+            throw new Exception("添加位置共享失败: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public void locationShareDel(Integer otherUserId) throws Exception {
+        try {
+            Integer userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            String key = getKey(userId, otherUserId);
+
+            // 存在key,则删除
+            if (baseRedisService.hasKey(key)) {
+                JSONArray array = JSONArray.parseArray(baseRedisService.getString(key));
+                array.remove(userId);
+                if (array.isEmpty()) {
+                    baseRedisService.delete(key);
+                    LifeUser user = lifeUserMapper.selectById(userId);
+                    LifeUser otherUser = lifeUserMapper.selectById(otherUserId);
+                    LambdaQueryWrapper<LifeMessage> wrapper = new LambdaQueryWrapper<>();
+                    wrapper.eq(LifeMessage::getType, 10);
+                    wrapper.isNull(LifeMessage::getContent);
+                    wrapper.apply("((receiver_id = 'user_" + otherUser.getUserPhone() + "' and sender_id = 'user_" + user.getUserPhone() + "') or (receiver_id = 'user_" + user.getUserPhone() + "' and sender_id = 'user_" + otherUser.getUserPhone() + "'))");
+                    List<Integer> messageIds = lifeMessageMapper.selectList(wrapper).stream().map(LifeMessage::getId).collect(Collectors.toList());
+
+                    if (CollectionUtils.isNotEmpty(messageIds)) {
+                        // 修改消息状态
+                        JSONObject messageJson = new JSONObject();
+                        messageJson.put("cardType", 2);
+                        LambdaUpdateWrapper<LifeMessage> updateWrapper = new LambdaUpdateWrapper<>();
+                        updateWrapper.in(LifeMessage::getId, messageIds);
+                        updateWrapper.set(LifeMessage::getContent, messageJson.toJSONString());
+                        lifeMessageMapper.update(null, updateWrapper);
+
+                        // 发起交易人信息
+                        String phoneId = JwtUtil.getCurrentUserInfo().getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");
+
+                        // 获取交易对方信息
+                        LifeUser lifeUser = lifeUserMapper.selectById(otherUserId);
+                        String receiverId = "user_" + lifeUser.getUserPhone();
+
+                        // 给买家与卖家发送交易消息
+                        WebSocketVo webSocketVo = new WebSocketVo();
+                        webSocketVo.setSenderId(phoneId);
+                        webSocketVo.setReceiverId(receiverId);
+                        webSocketVo.setCategory("message");
+                        webSocketVo.setType("10");
+                        webSocketVo.setIsRead(0);
+                        webSocketVo.setText(messageJson.toJSONString());
+                        webSocketVo.setMessageIdList(messageIds);
+
+                        alienStoreFeign.sendMsgToClientByPhoneId(phoneId, JSONObject.from(webSocketVo).toJSONString());
+                        alienStoreFeign.sendMsgToClientByPhoneId(receiverId, JSONObject.from(webSocketVo).toJSONString());
+                    }
+                } else {
+                    baseRedisService.setString(key, array.toJSONString());
+                }
+            }
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.locationShareDel(): Error Msg={}", e.getMessage(), e);
+            throw new Exception("删除位置共享失败: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public boolean locationShareHas(Integer otherUserId) throws Exception {
+        try {
+            Integer userId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId");
+            String key = getKey(userId, otherUserId);
+
+            // 是否存在定位信息
+            if (baseRedisService.hasKey(key) && !JSONArray.parseArray(baseRedisService.getString(key)).isEmpty()) {
+                return true;
+            } else {
+                baseRedisService.delete(key);
+            }
+
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.locationShareHas(): Error Msg={}", e.getMessage(), e);
+            throw new Exception("获取位置共享状态失败: " + e.getMessage());
+        }
+        return false;
+    }
+
+    private String getKey(Integer userId, Integer otherUserId) {
+        // 组合userId和otherUserId作为Redis的key
+        // 使用排序方式确保key的一致性(较小的ID在前)
+        String key;
+        if (userId.compareTo(otherUserId) < 0) {
+            key = "second_location_share:" + userId + ":" + otherUserId;
+        } else {
+            key = "second_location_share:" + otherUserId + ":" + userId;
+        }
+        return key;
+    }
+
+    /**
+     * 计算用户风控评分等级
+     * 根据用户违规次数计算等级:O(无记录) < A(1-2次) < B(3-5次) < C(6-9次) < D(10-14次) < E(≥15次)
+     *
+     * @param userId 用户ID
+     * @return 风控评分等级
+     */
+    private String calculateCreditLevel(Integer userId) {
+        try {
+            if (userId == null) {
+                return "O";
+            }
+            
+            // 查询用户违规记录数量
+            LambdaQueryWrapper<SecondRiskControlRecord> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(SecondRiskControlRecord::getUserId, userId)
+                    .eq(SecondRiskControlRecord::getDeleteFlag, 0);
+            
+            Integer count = secondRiskControlRecordMapper.selectCount(wrapper);
+            int violationCount = count != null ? count : 0;
+            
+            // 根据违规次数确定等级
+            if (violationCount == 0) {
+                return "O";  // 无记录
+            } else if (violationCount >= 15) {
+                return "E";  // 最差
+            } else if (violationCount >= 10) {
+                return "D";
+            } else if (violationCount >= 6) {
+                return "C";
+            } else if (violationCount >= 3) {
+                return "B";
+            } else {
+                return "A";  // 1-2次违规
+            }
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.calculateCreditLevel(): Error Msg={}", e.getMessage(), e);
+            return "O";  // 异常时返回默认等级
+        }
+    }
+}

+ 249 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondUserCreditRecordServiceImpl.java

@@ -0,0 +1,249 @@
+package shop.alien.second.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondUserCredit;
+import shop.alien.entity.second.SecondUserCreditDateRecord;
+import shop.alien.entity.second.SecondUserCreditRecord;
+import shop.alien.entity.second.vo.SecondUserCreditRecordListVo;
+import shop.alien.entity.store.LifeUserLearningRecord;
+import shop.alien.entity.store.LifeUserLearningVideo;
+import shop.alien.mapper.LifeUserLearningRecordMapper;
+import shop.alien.mapper.LifeUserLearningVideoMapper;
+import shop.alien.mapper.second.SecondUserCreditDateRecordMapper;
+import shop.alien.mapper.second.SecondUserCreditMapper;
+import shop.alien.mapper.second.SecondUserCreditRecordMapper;
+import shop.alien.second.service.SecondUserCreditRecordService;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 二手平台用户积分记录服务实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondUserCreditRecordServiceImpl extends ServiceImpl<SecondUserCreditRecordMapper, SecondUserCreditRecord> implements SecondUserCreditRecordService {
+    
+    private final SecondUserCreditRecordMapper secondUserCreditRecordMapper;
+    private final SecondUserCreditMapper secondUserCreditMapper;
+    private final SecondUserCreditDateRecordMapper secondUserCreditDateRecordMapper;
+    private final LifeUserLearningVideoMapper lifeUserLearningVideoMapper;
+    private final LifeUserLearningRecordMapper lifeUserLearningRecordMapper;
+
+    /**
+     * 根据用户ID获取积分记录列表
+     * @param userId 用户ID
+     * @return 积分记录列表
+     */
+    @Override
+    public List<SecondUserCreditRecord> getRecordsByUserId(Integer userId) {
+        log.info("查询用户积分记录,userId={}", userId);
+        LambdaQueryWrapper<SecondUserCreditRecord> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SecondUserCreditRecord::getUserId, userId)
+                .eq(SecondUserCreditRecord::getDeleteFlag, 0)
+                .orderByDesc(SecondUserCreditRecord::getCreatedTime);
+        return secondUserCreditRecordMapper.selectList(queryWrapper);
+    }
+    
+    /**
+     * 创建积分记录
+     * @param record 积分记录信息
+     * @return 是否创建成功
+     */
+    @Override
+    public boolean createRecord(SecondUserCreditRecord record) {
+        try {
+            log.info("创建积分记录,userId={}, points={}", record.getUserId(), record.getPoints());
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            record.setUpdatedTime(new Date());
+            int result = secondUserCreditRecordMapper.insert(record);
+            return result > 0;
+        } catch (Exception e) {
+            log.error("创建积分记录失败,userId={}, points={}", record.getUserId(), record.getPoints(), e);
+            return false;
+        }
+    }
+
+    @Override
+    public R<SecondUserCreditRecordListVo> getByUserId(Integer userId, Integer timeRange, Integer pointsType) {
+        log.info("SecondUserCreditRecordServiceImpl.getByUserId, userId={}, timeRange={}, pointsType={}", userId, timeRange, pointsType);
+        LambdaQueryWrapper<SecondUserCreditRecord> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SecondUserCreditRecord::getUserId, userId);
+
+        // 根据时间范围过滤
+        Date startTime = null;
+        if (timeRange != null) {
+            startTime = calculateStartTime(timeRange);
+            if (startTime != null) {
+                queryWrapper.ge(SecondUserCreditRecord::getCreatedTime, startTime);
+            }
+        }
+
+        // 根据积分类型过滤:0-全部,1-正分,2-负分
+        if (pointsType != null) {
+            if (pointsType == 1) {
+                // 正分:points > 0
+                queryWrapper.gt(SecondUserCreditRecord::getPoints, 0);
+            } else if (pointsType == 2) {
+                // 负分:points < 0
+                queryWrapper.lt(SecondUserCreditRecord::getPoints, 0);
+            }
+            // pointsType == 0 或 null 表示全部,不需要过滤
+        }
+
+        queryWrapper.orderByDesc(SecondUserCreditRecord::getCreatedTime);
+        List<SecondUserCreditRecord> recordsChange = this.list(queryWrapper);
+        
+        // 查询日期积分记录,按照相同的时间范围筛选
+        LambdaQueryWrapper<SecondUserCreditDateRecord> dateRecordQueryWrapper = new LambdaQueryWrapper<>();
+        dateRecordQueryWrapper.eq(SecondUserCreditDateRecord::getUserId, userId);
+        
+        if (startTime != null) {
+            // 将开始时间转换为日期(忽略时间部分)
+            Calendar cal = Calendar.getInstance();
+            cal.setTime(startTime);
+            cal.set(Calendar.HOUR_OF_DAY, 0);
+            cal.set(Calendar.MINUTE, 0);
+            cal.set(Calendar.SECOND, 0);
+            cal.set(Calendar.MILLISECOND, 0);
+            Date startDate = cal.getTime();
+            dateRecordQueryWrapper.ge(SecondUserCreditDateRecord::getRecordDate, startDate);
+        }
+        
+        dateRecordQueryWrapper.orderByDesc(SecondUserCreditDateRecord::getRecordDate);
+        List<SecondUserCreditDateRecord> dateRecords = secondUserCreditDateRecordMapper.selectList(dateRecordQueryWrapper);
+        
+        // 查询用户积分
+        Integer userPoints = null;
+        SecondUserCredit secondUserCredit = this.getUserCreditByUserId(userId);
+        if (secondUserCredit != null) {
+            userPoints = secondUserCredit.getUserPoints();
+        }
+        
+        // 构建返回对象
+        SecondUserCreditRecordListVo result = new SecondUserCreditRecordListVo();
+        result.setRecordsChange(recordsChange);
+        result.setDateRecords(dateRecords);
+        result.setUserPoints(userPoints);
+        return R.data(result);
+    }
+
+    /**
+     * 根据时间范围计算开始时间
+     * @param timeRange 时间范围:1-近一周,2-近一个月,3-近一年
+     * @return 开始时间
+     */
+    private Date calculateStartTime(Integer timeRange) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+
+        switch (timeRange) {
+            case 1: // 近一周
+                calendar.add(Calendar.DAY_OF_YEAR, -7);
+                break;
+            case 2: // 近一个月
+                calendar.add(Calendar.MONTH, -1);
+                break;
+            case 3: // 近一年
+                calendar.add(Calendar.YEAR, -1);
+                break;
+            default:
+                return null;
+        }
+
+        return calendar.getTime();
+    }
+
+    @Override
+    public R<String> addPointsAfterWatchingVideo(Integer userId, Integer learningId) {
+        log.info("用户观看完学习视频后增加积分,userId={}, learningId={}", userId, learningId);
+        
+        try {
+            // 1. 查询学习视频信息,获取奖励分数
+            LifeUserLearningVideo learningVideo = lifeUserLearningVideoMapper.selectById(learningId);
+            if (learningVideo == null) {
+                return R.fail("学习视频不存在");
+            }
+            
+            Integer score = learningVideo.getScore();
+            if (score == null || score <= 0) {
+                return R.fail("该学习视频没有奖励分数");
+            }
+            
+            // 2. 检查是否已经观看过(避免重复奖励)
+            LambdaQueryWrapper<LifeUserLearningRecord> recordQueryWrapper = new LambdaQueryWrapper<>();
+            recordQueryWrapper.eq(LifeUserLearningRecord::getUserId, userId)
+                    .eq(LifeUserLearningRecord::getLearningId, learningId)
+                    .eq(LifeUserLearningRecord::getDeleteFlag, 0);
+            long count = lifeUserLearningRecordMapper.selectCount(recordQueryWrapper);
+            if (count > 0) {
+                return R.fail("您已经观看过该视频,无法重复获得奖励");
+            }
+            
+            // 3. 查询或创建用户积分记录
+            SecondUserCredit userCredit = this.getUserCreditByUserId(userId);
+            if (userCredit == null) {
+                // 如果用户没有积分记录,先创建
+                userCredit = new SecondUserCredit();
+                userCredit.setUserId(userId);
+                userCredit.setUserPoints(0);
+                userCredit.setDeleteFlag(0);
+                userCredit.setCreatedTime(new Date());
+                userCredit.setUpdatedTime(new Date());
+                secondUserCreditMapper.insert(userCredit);
+            }
+            
+            // 4. 更新用户积分
+            secondUserCreditMapper.updatePointsByUserId(userId, score);
+            
+            // 5. 添加积分记录(pointsType = 8 表示学习视频奖励)
+            SecondUserCreditRecord creditRecord = new SecondUserCreditRecord();
+            creditRecord.setUserId(userId);
+            creditRecord.setPoints(score);
+            creditRecord.setPointsType(8); // 8-学习视频奖励
+            creditRecord.setDeleteFlag(0);
+            creditRecord.setCreatedTime(new Date());
+            creditRecord.setUpdatedTime(new Date());
+            secondUserCreditRecordMapper.insert(creditRecord);
+            
+            // 6. 添加学习记录
+            LifeUserLearningRecord learningRecord = new LifeUserLearningRecord();
+            learningRecord.setUserId(userId);
+            learningRecord.setLearningId(learningId);
+            learningRecord.setDeleteFlag(0);
+            learningRecord.setCreatedTime(new Date());
+            learningRecord.setUpdatedTime(new Date());
+            lifeUserLearningRecordMapper.insert(learningRecord);
+            
+            log.info("用户观看完学习视频后增加积分成功,userId={}, learningId={}, score={}", userId, learningId, score);
+            return R.success("观看视频成功,获得" + score + "积分");
+            
+        } catch (Exception e) {
+            log.error("用户观看完学习视频后增加积分失败,userId={}, learningId={}", userId, learningId, e);
+            return R.fail("操作失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据用户ID获取用户积分信息
+     * @param userId 用户ID
+     * @return 用户积分信息
+     */
+    private SecondUserCredit getUserCreditByUserId(Integer userId) {
+        LambdaQueryWrapper<SecondUserCredit> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SecondUserCredit::getUserId, userId)
+                .eq(SecondUserCredit::getDeleteFlag, 0);
+        return secondUserCreditMapper.selectOne(queryWrapper);
+    }
+}

+ 219 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/SecondUserCreditServiceImpl.java

@@ -0,0 +1,219 @@
+package shop.alien.second.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.second.SecondUserCredit;
+import shop.alien.entity.second.SecondUserCreditRecord;
+import shop.alien.entity.second.vo.SecondUserCreditVo;
+import shop.alien.mapper.second.SecondUserCreditMapper;
+import shop.alien.mapper.second.SecondUserCreditRecordMapper;
+import shop.alien.second.service.SecondGoodsService;
+import shop.alien.second.service.SecondUserCreditRecordService;
+import shop.alien.second.service.SecondUserCreditService;
+
+import java.util.Date;
+
+/**
+ * 二手平台用户积分服务实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondUserCreditServiceImpl extends ServiceImpl<SecondUserCreditMapper, SecondUserCredit> implements SecondUserCreditService {
+    
+    private final SecondUserCreditMapper secondUserCreditMapper;
+    private final SecondUserCreditRecordService secondUserCreditRecordService;
+    private final SecondUserCreditRecordMapper secondUserCreditRecordMapper;
+
+    /**
+     * 二手商品服务
+     */
+    private final SecondGoodsService secondGoodsService;
+
+    /**
+     * 根据用户ID获取用户积分信息
+     * @param userId 用户ID
+     * @return 用户积分信息
+     */
+    @Override
+    public SecondUserCredit getByUserId(Integer userId) {
+        LambdaQueryWrapper<SecondUserCredit> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SecondUserCredit::getUserId, userId)
+                .eq(SecondUserCredit::getDeleteFlag, 0);
+        return secondUserCreditMapper.selectOne(queryWrapper);
+    }
+    
+    /**
+     * 更新用户积分
+     * @param credit
+     * @return 是否更新成功
+     */
+    @Override
+    public boolean updatePoints(SecondUserCreditVo credit) {
+        try {
+            // 查询用户积分记录
+            SecondUserCredit userPoints = getByUserId(credit.getUserId());
+            
+            if (userPoints == null) {
+                // 如果用户没有积分记录,则创建新记录
+                userPoints = new SecondUserCredit();
+                userPoints.setUserId(credit.getUserId());
+                userPoints.setUserPoints(credit.getUserPoints() > 0 ? credit.getUserPoints() : 0);
+                userPoints.setDeleteFlag(0);
+                userPoints.setCreatedTime(new Date());
+                userPoints.setUpdatedTime(new Date());
+                secondUserCreditMapper.insert(userPoints);
+            } else {
+                // 更新积分
+                int newPoints = userPoints.getUserPoints() - 50;
+                userPoints.setUserPoints(newPoints > 0 ? newPoints : 0);
+                userPoints.setUpdatedTime(new Date());
+                secondUserCreditMapper.updateById(userPoints);
+            }
+            if (credit.getRuleType() == 3 || credit.getRuleType() == 4) {
+                secondGoodsService.batchShelveGoodsByRiskControlRecord(credit.getRuleType(), credit.getBusinessId());
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("更新用户积分失败,credit={}", credit, e);
+            return false;
+        }
+    }
+    
+    /**
+     * 新建用户积分记录
+     * @param userId 用户ID
+     * @param initialPoints 初始积分值
+     * @return 是否创建成功
+     */
+    @Override
+    public boolean createPointsRecord(Integer userId, Integer initialPoints, Integer pointsType) {
+        try {
+            // 检查是否已存在记录
+            SecondUserCredit existingPoints = getByUserId(userId);
+            if (existingPoints != null) {
+                // 使用LambdaQueryWrapper的exists方法 查看是否已添加基础积分
+                LambdaQueryWrapper<SecondUserCreditRecord> queryWrapper = new LambdaQueryWrapper<>();
+                queryWrapper.eq(SecondUserCreditRecord::getUserId, userId)
+                        .eq(SecondUserCreditRecord::getPointsType, 1)
+                        .eq(SecondUserCreditRecord::getDeleteFlag, 0);
+                boolean exists = secondUserCreditRecordMapper.selectCount(queryWrapper) > 0;
+
+                if (!exists) {
+                    // 更新积分
+                    int newPoints = existingPoints.getUserPoints() + initialPoints;
+                    existingPoints.setUserPoints(newPoints > 0 ? newPoints : 0);
+                    existingPoints.setUpdatedTime(new Date());
+                    secondUserCreditMapper.updateById(existingPoints);
+
+                    // 创建积分记录
+                    SecondUserCreditRecord record = new SecondUserCreditRecord();
+                    record.setUserId(userId);
+                    record.setPoints(initialPoints);
+                    record.setPointsType(pointsType);
+                    secondUserCreditRecordService.createRecord(record);
+                    return true;
+                }
+
+                log.warn("用户积分记录已存在,userId={}", userId);
+                return false;
+            }
+            return addPointsRecord(userId, initialPoints, pointsType);
+        } catch (Exception e) {
+            log.error("创建用户积分记录失败,userId={}, initialPoints={}", userId, initialPoints, e);
+            return false;
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean addIdInfoPoints(Integer userId) throws Exception {
+        try {
+            LambdaQueryWrapper<SecondUserCreditRecord> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(SecondUserCreditRecord::getUserId, userId);
+            wrapper.eq(SecondUserCreditRecord::getPointsType, 2);
+            if (secondUserCreditRecordMapper.selectCount(wrapper) == 0) {
+                SecondUserCreditRecord record = new SecondUserCreditRecord();
+                record.setUserId(userId);
+                record.setPoints(200);
+                record.setPointsType(2);
+                secondUserCreditRecordMapper.insert(record);
+
+                LambdaQueryWrapper<SecondUserCredit> queryWrapper = new LambdaQueryWrapper<>();
+                queryWrapper.eq(SecondUserCredit::getUserId, userId);
+                SecondUserCredit userCredit = secondUserCreditMapper.selectOne(queryWrapper);
+                if (userCredit != null) {
+                    secondUserCreditMapper.updatePointsByUserId(userId, 200);
+                } else {
+                    SecondUserCredit credit = new SecondUserCredit();
+                    credit.setUserId(userId);
+                    credit.setUserPoints(200);
+                    secondUserCreditMapper.insert(credit);
+                }
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("SecondUserCreditServiceImpl.addIdInfoPoints(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    @Override
+    public JSONObject isFree(Integer userId) throws Exception {
+        try {
+            LambdaQueryWrapper<SecondUserCredit> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(SecondUserCredit::getUserId, userId);
+            SecondUserCredit secondUserCredit = secondUserCreditMapper.selectOne(queryWrapper);
+
+            JSONObject jsonObject = new JSONObject();
+            if (secondUserCredit == null || secondUserCredit.getFreezeTime().after(new Date())) {
+                jsonObject.put("flag", false);
+                jsonObject.put("msg", "无法使用该功能");
+            } else if (secondUserCredit.getFreezeTime() == null || secondUserCredit.getUserPoints() < 500) {
+                jsonObject.put("flag", false);
+                jsonObject.put("msg", "信用分不足无法使用该功能");
+            } else  {
+                jsonObject.put("flag", true);
+            }
+
+            return jsonObject;
+        } catch (Exception e) {
+            log.error("SecondUserCreditServiceImpl.isFree(): Error Msg={}", e.getMessage());
+            throw new Exception(e);
+        }
+    }
+
+    public boolean addPointsRecord(Integer userId, Integer points, Integer pointsType) {
+        try {
+            // 创建新的积分记录
+            SecondUserCredit userPoints = new SecondUserCredit();
+            userPoints.setUserId(userId);
+            userPoints.setUserPoints(points != null ? points : 0);
+            userPoints.setDeleteFlag(0);
+            userPoints.setCreatedTime(new Date());
+            userPoints.setUpdatedTime(new Date());
+
+            int result = secondUserCreditMapper.insert(userPoints);
+
+            // 创建积分记录
+            SecondUserCreditRecord record = new SecondUserCreditRecord();
+            record.setUserId(userId);
+            record.setPoints(points);
+            record.setPointsType(pointsType);
+            secondUserCreditRecordMapper.insert(record);
+
+            return result > 0;
+        } catch (Exception e) {
+            log.error("删除用户积分记录失败,userId={}", userId, e);
+            return false;
+        }
+    }
+
+
+}

+ 275 - 0
alien-branch-second/src/main/java/shop/alien/second/service/impl/VideoModerationServiceImpl.java

@@ -0,0 +1,275 @@
+package shop.alien.second.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.aliyun.green20220302.models.VideoModerationResponse;
+import com.aliyun.green20220302.models.VideoModerationResultResponse;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.SecondVideoTask;
+import shop.alien.mapper.system.SecondVideoTaskMapper;
+import shop.alien.second.service.SecondGoodsService;
+import shop.alien.second.service.VideoModerationService;
+import shop.alien.util.common.safe.video.VideoModerationUtil;
+
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * 视频审核业务服务类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class VideoModerationServiceImpl extends ServiceImpl<SecondVideoTaskMapper, SecondVideoTask> implements VideoModerationService {
+
+    /**
+     * 视频审核工具类
+     */
+    private final VideoModerationUtil videoModerationUtil;
+
+    /**
+     * 视频审核任务Mapper
+     */
+    private final SecondVideoTaskMapper videoModerationTaskMapper;
+
+    /**
+     * 二手商品服务(使用@Lazy避免循环依赖)
+     */
+    @Lazy
+    @Autowired
+    private SecondGoodsService secondGoodsService;
+
+    /**
+     * 提交视频审核任务
+     *
+     * @param videoUrl 视频URL
+     * @return 任务ID
+     */
+    public String submitVideoModerationTask(String videoUrl) {
+        try {
+            // 生成dataId
+            String dataId = "video_" + UUID.randomUUID().toString().replace("-", "");
+
+            // 调用阿里云接口提交任务
+            VideoModerationResponse response = videoModerationUtil.submitVideoModerationTask(videoUrl, dataId);
+
+            if (response.getStatusCode() == 200 && response.getBody() != null &&
+                    response.getBody().getCode() != null && response.getBody().getCode() == 200) {
+
+                // 保存任务信息到数据库
+                SecondVideoTask task = new SecondVideoTask();
+                task.setDataId(dataId);
+                task.setVideoUrl(videoUrl);
+                task.setTaskId(response.getBody().getData().getTaskId());
+                task.setStatus("SUBMITTED");
+                task.setCreateTime(new Date());
+                task.setUpdateTime(new Date());
+
+                videoModerationTaskMapper.insert(task);
+
+                log.info("视频审核任务提交成功,任务ID: {}", task.getTaskId());
+                return task.getTaskId();
+            } else {
+                log.error("提交视频审核任务失败,响应: {}", JSON.toJSONString(response));
+                throw new RuntimeException("提交视频审核任务失败: " + (response.getBody() != null ? response.getBody().getMessage() : "未知错误"));
+            }
+        } catch (Exception e) {
+            log.error("提交视频审核任务异常", e);
+            throw new RuntimeException("提交视频审核任务异常: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据任务ID查询任务详情
+     *
+     * @param taskId 任务ID
+     * @return 任务详情
+     */
+    public SecondVideoTask getTaskByTaskId(String taskId) {
+        try {
+            return videoModerationTaskMapper.selectByTaskId(taskId);
+        } catch (Exception e) {
+            log.error("查询视频审核任务详情异常,任务ID: {}", taskId, e);
+            throw new RuntimeException("查询视频审核任务详情异常: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 根据任务ID查询任务详情
+     * 
+     * @param taskId 任务ID
+     * @return 任务详情
+     */
+    public SecondVideoTask getTaskById(String taskId) {
+        try {
+            return videoModerationTaskMapper.selectByTaskId(taskId);
+        } catch (Exception e) {
+            log.error("查询视频审核任务详情异常,任务ID: {}", taskId, e);
+            throw new RuntimeException("查询视频审核任务详情异常: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 查询视频审核结果
+     *
+     * @param taskId 任务ID
+     * @return 审核结果
+     */
+    public SecondVideoTask queryVideoModerationResult(String taskId) {
+        try {
+            // 查询数据库中的任务
+            SecondVideoTask task = videoModerationTaskMapper.selectByTaskId(taskId);
+            if (task == null) {
+                throw new RuntimeException("未找到任务ID为 " + taskId + " 的审核任务");
+            }
+
+            // 调用阿里云接口获取审核结果
+            VideoModerationResultResponse response = videoModerationUtil.getVideoModerationResult(taskId);
+
+            if (response.getStatusCode() == 200 && response.getBody() != null &&
+                    response.getBody().getCode() != null && response.getBody().getCode() == 200) {
+
+                // 更新数据库中的任务结果
+                String status = "SUCCESS";
+                String riskLevel = response.getBody().getData().getRiskLevel();
+                String result = JSON.toJSONString(response.getBody().getData());
+
+                videoModerationTaskMapper.updateTaskResult(taskId, status, riskLevel, result);
+
+                // 更新任务对象并返回
+                task.setStatus(status);
+                task.setRiskLevel(riskLevel);
+                task.setResult(result);
+                task.setUpdateTime(new Date());
+
+                log.info("获取视频审核结果成功,任务ID: {}", taskId);
+                return task;
+            } else {
+                log.error("获取视频审核结果失败,响应: {}", JSON.toJSONString(response));
+                throw new RuntimeException("获取视频审核结果失败: " + (response.getBody() != null ? response.getBody().getMessage() : "未知错误"));
+            }
+        } catch (Exception e) {
+            log.error("获取视频审核结果异常,任务ID: {}", taskId, e);
+            throw new RuntimeException("获取视频审核结果异常: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 处理单个审核任务
+     *
+     * @param task 审核任务
+     * @return 是否处理成功
+     */
+    public boolean processTask(SecondVideoTask task) {
+        try {
+            // 调用阿里云接口获取审核结果
+            VideoModerationResultResponse response = videoModerationUtil.getVideoModerationResult(task.getTaskId());
+
+            if (response.getStatusCode() == 200 && response.getBody() != null) {
+                Integer code = response.getBody().getCode();
+                if (code != null && code == 200) {
+                    // 审核完成,更新任务结果
+                    String status = "SUCCESS";
+                    String riskLevel = response.getBody().getData().getRiskLevel();
+                    String result = JSON.toJSONString(response.getBody().getData());
+
+                    videoModerationTaskMapper.updateTaskResult(task.getTaskId(), status, riskLevel, result);
+
+                    log.info("视频审核任务处理成功,任务ID: {}", task.getTaskId());
+                    return true;
+                } else if (code != null && code == 500) {
+                    // 审核仍在进行中,更新状态
+                    videoModerationTaskMapper.updateTaskStatus(task.getTaskId(), "PROCESSING");
+                    log.info("视频审核任务仍在处理中,任务ID: {}", task.getTaskId());
+                    return false;
+                } else {
+                    // 审核失败
+                    videoModerationTaskMapper.updateTaskStatus(task.getTaskId(), "PROCESSING");
+                    log.error("视频审核任务处理失败,任务ID: {},错误信息: {}", task.getTaskId(), response.getBody().getMessage());
+                    return false;
+                }
+            } else {
+                // HTTP请求失败
+                log.error("获取视频审核结果HTTP请求失败,任务ID: {},状态码: {}", task.getTaskId(), response.getStatusCode());
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("处理视频审核任务异常,任务ID: {}", task.getTaskId(), e);
+
+            // 增加重试次数
+            videoModerationTaskMapper.incrementRetryCount(task.getId());
+
+            // 如果重试次数超过3次,标记为失败
+            if (task.getRetryCount() >= 3) {
+                videoModerationTaskMapper.updateTaskStatus(task.getTaskId(), "FAILED");
+            }
+
+            return false;
+        }
+    }
+
+    /**
+     * 处理所有待处理的视频审核任务
+     * @return 处理结果
+     */
+    @Override
+    public String processAllPendingVideoTasks() {
+        try {
+            log.info("开始执行视频审核结果处理");
+
+            // 查询待处理的审核任务(状态为SUBMITTED或PROCESSING)
+            List<SecondVideoTask> pendingTasks = videoModerationTaskMapper.selectPendingTasks();
+
+            if (pendingTasks.isEmpty()) {
+                log.info("没有待处理的视频审核任务");
+                return "没有待处理的视频审核任务";
+            }
+
+            log.info("共找到 {} 个待处理的视频审核任务", pendingTasks.size());
+
+            int successCount = 0;
+            int processingCount = 0;
+            int failCount = 0;
+
+            for (SecondVideoTask task : pendingTasks) {
+                try {
+                    // 1. 拉取视频审核结果
+                    boolean success = processTask(task);
+                    if (success) {
+                        successCount++;
+                        // 2. 视频审核完成后,更新商品业务状态
+                        log.info("TaskId:开始处理商品状态 {}", task.getTaskId());
+                        SecondVideoTask updatedTask = videoModerationTaskMapper.selectByTaskId(task.getTaskId());
+                        if (updatedTask != null) {
+                            secondGoodsService.processVideoModerationResult(updatedTask);
+                        }
+                        log.info("TaskId:商品状态处理完成 {}", task.getTaskId());
+                    } else {
+                        // 检查任务状态判断是处理中还是失败
+                        SecondVideoTask updatedTask = videoModerationTaskMapper.selectByTaskId(task.getTaskId());
+                        if (updatedTask != null && "PROCESSING".equals(updatedTask.getStatus())) {
+                            processingCount++;
+                        } else {
+                            failCount++;
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("处理视频审核任务时发生异常,任务ID: {}", task.getTaskId(), e);
+                    failCount++;
+                }
+            }
+
+            String result = String.format("视频审核结果处理完成,成功:%d,处理中:%d,失败:%d", successCount, processingCount, failCount);
+            log.info(result);
+            return result;
+        } catch (Exception e) {
+            log.error("执行视频审核结果处理任务时发生异常", e);
+            return "执行异常: " + e.getMessage();
+        }
+    }
+}

+ 382 - 0
alien-branch-second/src/main/java/shop/alien/second/task/Task.java

@@ -0,0 +1,382 @@
+package shop.alien.second.task;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import shop.alien.config.redis.BaseRedisService;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.SecondTradeOperation;
+import shop.alien.entity.second.SecondTradeRecord;
+import shop.alien.entity.store.LifeMessage;
+import shop.alien.entity.store.LifeNotice;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.mapper.LifeMessageMapper;
+import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.mapper.LifeUserMapper;
+import shop.alien.mapper.second.SecondGoodsMapper;
+import shop.alien.mapper.second.SecondTradeOperationMapper;
+import shop.alien.mapper.second.SecondTradeRecordMapper;
+import shop.alien.second.feign.AlienStoreFeign;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class Task {
+
+    private final SecondTradeRecordMapper secondTradeRecordMapper;
+
+    private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeUserMapper lifeUserMapper;
+
+    private final LifeMessageMapper lifeMessageMapper;
+
+    private final AlienStoreFeign alienStoreFeign;
+
+    private final BaseRedisService baseRedisService;
+
+    private final SecondGoodsMapper secondGoodsMapper;
+
+    private final SecondTradeOperationMapper secondTradeOperationMapper;
+
+    @Value("${ScheduledTask.enabled}")
+    private boolean isEnable;
+
+    /**
+     * 二手交易平台 - 交易时间前的十分钟之前  给买家和卖家发送通知提醒
+     */
+    @Scheduled(cron = "0 * * * * ?")
+    public void secondTradeRemind() throws Exception {
+        if (!isEnable) {
+            return;
+        }
+
+        log.info("开始执行定时任务: 二手交易平台 - 10分钟后交易提醒 - secondTradeRemind");
+        try {
+            Date tenMinuteLater = Date.from(LocalDateTime.now().plusMinutes(10).withSecond(0).withNano(0).atZone(ZoneId.systemDefault()).toInstant());
+
+            // 查询所有待交易
+            LambdaQueryWrapper<SecondTradeRecord> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(SecondTradeRecord::getTradeStatus, 3);
+            wrapper.eq(SecondTradeRecord::getTransactionTime, tenMinuteLater);
+            List<SecondTradeRecord> tradeRecordList = secondTradeRecordMapper.selectList(wrapper);
+
+            for (SecondTradeRecord tradeRecord : tradeRecordList) {
+                LifeUser buyer = lifeUserMapper.selectById(tradeRecord.getBuyerId());
+                LifeUser seller = lifeUserMapper.selectById(tradeRecord.getSellerId());
+
+                String buyerPhoneId = null == buyer ? "" : "user_" + buyer.getUserPhone();
+                String sellerPhoneId = null == seller ? "" : "user_" + seller.getUserPhone();
+
+                // 封装交易信息
+                JSONObject message = new JSONObject();
+                message.put("tradeId", tradeRecord.getId());
+                message.put("transactionAmount", tradeRecord.getTransactionAmount());
+                message.put("transactionLatitudeLongitude", tradeRecord.getTransactionLatitudeLongitude());
+                message.put("transactionLatitudeLongitudeAddress", tradeRecord.getTransactionLatitudeLongitudeAddress());
+                message.put("transactionLocation", tradeRecord.getTransactionLocation());
+                message.put("transactionTime", tradeRecord.getTransactionTime());
+                message.put("tradeStatus", tradeRecord.getTradeStatus());
+
+                // 给买家发送消息
+                LifeMessage lifeMessage = new LifeMessage();
+                lifeMessage.setSenderId(sellerPhoneId);
+                lifeMessage.setReceiverId(buyerPhoneId);
+                lifeMessage.setContent(message.toJSONString());
+                lifeMessage.setType("5");
+                lifeMessageMapper.insert(lifeMessage);
+
+                // 给买家推送消息
+                WebSocketVo webSocketVo = new WebSocketVo();
+                webSocketVo.setSenderId(sellerPhoneId);
+                webSocketVo.setReceiverId(buyerPhoneId);
+                webSocketVo.setCategory("message");
+                webSocketVo.setType("5");
+                webSocketVo.setText(message.toJSONString());
+                webSocketVo.setMessageId(lifeMessage.getId());
+                alienStoreFeign.sendMsgToClientByPhoneId(buyerPhoneId, JSONObject.from(webSocketVo).toJSONString());
+
+                // 给买家发送通知
+                LifeNotice lifeNotice = new LifeNotice();
+                lifeNotice.setSenderId("system");
+                lifeNotice.setReceiverId(buyerPhoneId);
+                lifeNotice.setBusinessId(tradeRecord.getId());
+                lifeNotice.setTitle("商品交易签到");
+                lifeNotice.setNoticeType(1);
+                // 封装通知信息
+                JSONObject noticeMessage = new JSONObject();
+                noticeMessage.put("tradeId", tradeRecord.getId());
+                noticeMessage.put("otherSideUserId", tradeRecord.getSellerId());
+                noticeMessage.put("otherSidePhoneId", sellerPhoneId);
+                noticeMessage.put("otherSideName", null == seller ? "" : seller.getUserName());
+                noticeMessage.put("otherSideImage", null == seller ? "" : seller.getUserImage());
+                noticeMessage.put("message", "您有一笔交易即将开始, 请及时前往查看");
+                lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeMapper.insert(lifeNotice);
+
+                // 给买家推送通知
+                webSocketVo = new WebSocketVo();
+                webSocketVo.setSenderId("system");
+                webSocketVo.setReceiverId(buyerPhoneId);
+                webSocketVo.setCategory("notice");
+                webSocketVo.setNoticeType("1");
+                webSocketVo.setType("5");
+                webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+                webSocketVo.setMessageId(lifeMessage.getId());
+                alienStoreFeign.sendMsgToClientByPhoneId(buyerPhoneId, JSONObject.from(webSocketVo).toJSONString());
+
+                // 给卖家发送消息
+                lifeMessage = new LifeMessage();
+                lifeMessage.setSenderId(buyerPhoneId);
+                lifeMessage.setReceiverId(sellerPhoneId);
+                lifeMessage.setContent(message.toJSONString());
+                lifeMessage.setType("5");
+                lifeMessageMapper.insert(lifeMessage);
+
+                // 给卖家推送消息
+                webSocketVo = new WebSocketVo();
+                webSocketVo.setSenderId(buyerPhoneId);
+                webSocketVo.setReceiverId(sellerPhoneId);
+                webSocketVo.setCategory("message");
+                webSocketVo.setType("5");
+                webSocketVo.setText(message.toJSONString());
+                webSocketVo.setMessageId(lifeMessage.getId());
+                alienStoreFeign.sendMsgToClientByPhoneId(sellerPhoneId, JSONObject.from(webSocketVo).toJSONString());
+
+                // 给卖家发送通知
+                lifeNotice = new LifeNotice();
+                lifeNotice.setSenderId("system");
+                lifeNotice.setReceiverId(sellerPhoneId);
+                lifeNotice.setBusinessId(tradeRecord.getId());
+                lifeNotice.setTitle("商品交易签到");
+                lifeNotice.setNoticeType(1);
+                // 封装通知信息
+                noticeMessage = new JSONObject();
+                noticeMessage.put("tradeId", tradeRecord.getId());
+                noticeMessage.put("otherSideUserId", tradeRecord.getBuyerId());
+                noticeMessage.put("otherSidePhoneId", buyerPhoneId);
+                noticeMessage.put("otherSideName", null == buyer ? "" : buyer.getUserName());
+                noticeMessage.put("otherSideImage", null == buyer ? "" : buyer.getUserImage());
+                noticeMessage.put("message", "您有一笔交易即将开始, 请及时前往查看");
+                lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeMapper.insert(lifeNotice);
+
+                // 给卖家推送通知
+                webSocketVo = new WebSocketVo();
+                webSocketVo.setSenderId("system");
+                webSocketVo.setReceiverId(sellerPhoneId);
+                webSocketVo.setCategory("notice");
+                webSocketVo.setNoticeType("1");
+                webSocketVo.setType("5");
+                webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+                webSocketVo.setMessageId(lifeMessage.getId());
+                alienStoreFeign.sendMsgToClientByPhoneId(sellerPhoneId, JSONObject.from(webSocketVo).toJSONString());
+            }
+        } catch (Exception e) {
+            log.error("SecondGoodsTradeXxlJob.secondTradeRemind Error Msg={}", e.getMessage());
+        }
+    }
+
+    /**
+     * 二手交易平台 - 到达交易时间时,给买家和卖家发送交易确认提醒
+     */
+    @Scheduled(cron = "0 * * * * ?")
+    public void secondTradeConfirm() throws Exception {
+        if (!isEnable) {
+            return;
+        }
+
+        log.info("开始执行定时任务: 二手交易平台 - 到达交易时间时,给买家和卖家发送交易确认提醒 - secondTradeConfirm");
+        try {
+            Date now = Date.from(LocalDateTime.now().withSecond(0).withNano(0).atZone(ZoneId.systemDefault()).toInstant());
+
+            // 查询所有待交易
+            LambdaQueryWrapper<SecondTradeRecord> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(SecondTradeRecord::getTradeStatus, 3);
+            wrapper.eq(SecondTradeRecord::getTransactionTime, now);
+            List<SecondTradeRecord> tradeRecordList = secondTradeRecordMapper.selectList(wrapper);
+
+            for (SecondTradeRecord tradeRecord : tradeRecordList) {
+                LifeUser buyer = lifeUserMapper.selectById(tradeRecord.getBuyerId());
+                LifeUser seller = lifeUserMapper.selectById(tradeRecord.getSellerId());
+                SecondGoods goods = secondGoodsMapper.selectById(tradeRecord.getGoodsId());
+
+                String buyerPhoneId = null == buyer ? "" : "user_" + buyer.getUserPhone();
+                String sellerPhoneId = null == seller ? "" : "user_" + seller.getUserPhone();
+
+                // 给买家发送通知
+                LifeNotice lifeNotice = new LifeNotice();
+                lifeNotice.setSenderId("system");
+                lifeNotice.setReceiverId(buyerPhoneId);
+                lifeNotice.setBusinessId(tradeRecord.getId());
+                lifeNotice.setTitle("商品是否交易成功");
+                lifeNotice.setNoticeType(1);
+                // 封装通知信息
+                JSONObject noticeMessage = new JSONObject();
+                noticeMessage.put("goodsId", goods.getId());
+                noticeMessage.put("goodsHomeImage", goods.getHomeImage());
+                noticeMessage.put("goodsTitle", goods.getTitle());
+                noticeMessage.put("tradeId", tradeRecord.getId());
+                noticeMessage.put("transactionAmount", tradeRecord.getTransactionAmount());
+                noticeMessage.put("transactionLatitudeLongitude", tradeRecord.getTransactionLatitudeLongitude());
+                noticeMessage.put("transactionLatitudeLongitudeAddress", tradeRecord.getTransactionLatitudeLongitudeAddress());
+                noticeMessage.put("transactionLocation", tradeRecord.getTransactionLocation());
+                noticeMessage.put("transactionTime", tradeRecord.getTransactionTime());
+                noticeMessage.put("tradeStatus", tradeRecord.getTradeStatus());
+                noticeMessage.put("message", "您有一笔交易已完成, 请前往确认");
+                lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeMapper.insert(lifeNotice);
+
+                // 给买家推送通知
+                WebSocketVo webSocketVo = new WebSocketVo();
+                webSocketVo.setSenderId("system");
+                webSocketVo.setReceiverId(buyerPhoneId);
+                webSocketVo.setCategory("notice");
+                webSocketVo.setNoticeType("1");
+                webSocketVo.setType("5");
+                webSocketVo.setIsRead(0);
+                webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+                alienStoreFeign.sendMsgToClientByPhoneId(buyerPhoneId, JSONObject.from(webSocketVo).toJSONString());
+
+                // 给卖家发送通知
+                lifeNotice = new LifeNotice();
+                lifeNotice.setSenderId("system");
+                lifeNotice.setReceiverId(sellerPhoneId);
+                lifeNotice.setBusinessId(tradeRecord.getId());
+                lifeNotice.setTitle("商品是否交易成功");
+                lifeNotice.setNoticeType(1);
+                lifeNotice.setContext(noticeMessage.toJSONString());
+                lifeNoticeMapper.insert(lifeNotice);
+
+                // 给卖家推送通知
+                webSocketVo = new WebSocketVo();
+                webSocketVo.setSenderId("system");
+                webSocketVo.setReceiverId(sellerPhoneId);
+                webSocketVo.setCategory("notice");
+                webSocketVo.setNoticeType("1");
+                webSocketVo.setType("5");
+                webSocketVo.setIsRead(0);
+                webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+                alienStoreFeign.sendMsgToClientByPhoneId(sellerPhoneId, JSONObject.from(webSocketVo).toJSONString());
+            }
+        } catch (Exception e) {
+            log.error("SecondGoodsTradeXxlJob.secondTradeConfirm Error Mgs={}", e.getMessage());
+        }
+    }
+
+    /**
+     * 二手交易平台 - 交易超时未确认则自动取消
+     */
+    @Scheduled(cron = "0 * * * * ?")
+    private void secondTradeTimeoutCancel() {
+        if (!isEnable) {
+            return;
+        }
+
+        log.info("开始执行定时任务: 二手交易平台 - 交易超时未确认则自动取消 - secondTradeTimeoutCancel");
+        try {
+            Date now = Date.from(LocalDateTime.now().withSecond(0).withNano(0).atZone(ZoneId.systemDefault()).toInstant());
+
+            // 查询所有待确认
+            LambdaQueryWrapper<SecondTradeRecord> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(SecondTradeRecord::getTradeStatus, 1);
+            queryWrapper.eq(SecondTradeRecord::getTransactionTime, now);
+            List<SecondTradeRecord> tradeRecordList = secondTradeRecordMapper.selectList(queryWrapper);
+            List<Integer> tradeIdList = tradeRecordList.stream().map(SecondTradeRecord::getId).collect(Collectors.toList());
+            if (CollectionUtil.isNotEmpty(tradeIdList)) {
+                LambdaUpdateWrapper<SecondTradeRecord> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.in(SecondTradeRecord::getId, tradeIdList)
+                        .set(SecondTradeRecord::getTradeStatus, 6);
+                secondTradeRecordMapper.update(null, updateWrapper);
+
+                for (Integer id : tradeIdList) {
+                    SecondTradeOperation operation = new SecondTradeOperation();
+                    operation.setTradeId(id);
+                    operation.setUserId(0);
+                    operation.setType(7);
+                    secondTradeOperationMapper.insert(operation);
+                }
+            }
+        } catch (Exception e) {
+            log.error("SecondGoodsTradeXxlJob.secondTradeTimeoutCancel Error Mgs={}", e.getMessage());
+        }
+    }
+
+    /**
+     * 二手交易平台 - 到达交易时间12个小时后,自动交易失败
+     */
+    @Scheduled(cron = "0 * * * * ?")
+    private void secondTradeTimeoutFail() {
+        if (!isEnable) {
+            return;
+        }
+
+        log.info("开始执行定时任务: 二手交易平台 - 到达交易时间12个小时后,自动交易失败 - secondTradeTimeoutFail");
+        try {
+            Date twelveBefore = Date.from(LocalDateTime.now().plusHours(12).withSecond(0).withNano(0).atZone(ZoneId.systemDefault()).toInstant());
+
+            // 查询所有待确认
+            LambdaQueryWrapper<SecondTradeRecord> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(SecondTradeRecord::getTradeStatus, 3);
+            queryWrapper.eq(SecondTradeRecord::getTransactionTime, twelveBefore);
+            List<SecondTradeRecord> tradeRecordList = secondTradeRecordMapper.selectList(queryWrapper);
+            List<Integer> tradeIdList = tradeRecordList.stream().map(SecondTradeRecord::getId).collect(Collectors.toList());
+            if (CollectionUtil.isNotEmpty(tradeIdList)) {
+                LambdaUpdateWrapper<SecondTradeRecord> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.in(SecondTradeRecord::getId, tradeIdList)
+                        .set(SecondTradeRecord::getTradeStatus, 5);
+                secondTradeRecordMapper.update(null, updateWrapper);
+
+                for (Integer id : tradeIdList) {
+                    SecondTradeOperation operation = new SecondTradeOperation();
+                    operation.setTradeId(id);
+                    operation.setUserId(0);
+                    operation.setType(6);
+                    operation.setCreatedTime(new Date());
+                    secondTradeOperationMapper.insert(operation);
+                }
+            }
+        } catch (Exception e) {
+            log.error("SecondGoodsTradeXxlJob.secondTradeTimeoutFail Error Mgs={}", e.getMessage());
+        }
+    }
+
+    /**
+     * 二手交易平台 - 每分钟从redis中读取已读的消息id 并将数据库设为已读
+     */
+    @Scheduled(cron = "0 * * * * ?")
+    public void readMessage() {
+        if (!isEnable) {
+            return;
+        }
+
+        log.info("开始执行定时任务: 二手交易平台 - 每分钟从redis中读取已读的消息id 并将数据库设为已读 - readMessage");
+        try {
+            if (CollectionUtil.isEmpty(baseRedisService.getList("readMessageIdKey"))) return;
+
+            List<String> dataList = baseRedisService.popBatchFromList("readMessageIdKey");
+            if (CollectionUtil.isNotEmpty(dataList)) {
+                LambdaUpdateWrapper<LifeMessage> wrapper = new LambdaUpdateWrapper<>();
+                wrapper.in(LifeMessage::getId, dataList)
+                        .set(LifeMessage::getIsRead, 1);
+                lifeMessageMapper.update(null, wrapper);
+            }
+        } catch (Exception e) {
+            log.error("ScheduledTask.readMessage Error Mgs={}", e.getMessage());
+        }
+    }
+}

+ 153 - 0
alien-branch-second/src/main/java/shop/alien/second/util/AiTaskUtils.java

@@ -0,0 +1,153 @@
+package shop.alien.second.util;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.map.HashedMap;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.http.*;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreCommentAppeal;
+
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+@RefreshScope
+public class AiTaskUtils {
+
+    private final RestTemplate restTemplate;
+
+//    @Value("${third-party-login.base-url:http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login}")
+//    private String loginUrl = "http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login";
+
+    @Value("${third-party-user-name.base-url:UdUser}")
+    private String userName;
+
+    @Value("${third-party-pass-word.base-url:123456}")
+    private String passWord;
+
+
+    @Value("${third-party-login.base-url:http://192.168.2.250:9100/ai/user-auth-core/api/v1/auth/login}")
+    private String loginUrl;
+
+    @Value("${audit.auditTaskUrl:http://192.168.2.250:9100/ai/auto-review/api/v1/audit_task/product}")
+    private String auditTaskUrl;
+//    private String auditTaskUrl = "http://192.168.2.250:9000/ai/auto-review/api/v1/audit_task/product";
+
+
+    private String auditTaskResultUrl = "http://192.168.2.250:9000/ai/task-core/api/v1/audit_task/getResult";
+
+    /**
+     * 登录 AI 服务,获取 token
+     *
+     * @return accessToken
+     */
+    public String getAccessToken() {
+        log.info("登录Ai服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        ResponseEntity<String> response;
+        try {
+            log.info("请求Ai服务登录接口===================>");
+            response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+        } catch (Exception e) {
+            log.error("请求AI服务登录接口失败", e);
+            return null;
+        }
+
+        if (response != null && response.getStatusCode() == HttpStatus.OK) {
+            String body = response.getBody();
+            log.info("请求Ai服务登录成功 postForEntity.getBody()\t{}", body);
+            if (StringUtils.hasText(body)) {
+                JSONObject jsonObject = JSONObject.parseObject(body);
+                if (jsonObject != null) {
+                    JSONObject dataJson = jsonObject.getJSONObject("data");
+                    if (dataJson != null) {
+                        return dataJson.getString("access_token");
+                    }
+                }
+            }
+            log.warn("AI服务登录响应解析失败 body: {}", body);
+            return null;
+        }
+
+        log.error("请求AI服务 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        return null;
+    }
+
+
+    /**
+     * 调用AI服务创建任务
+     *
+     * @return accessToken
+     */
+    public String createTask(String accessToken, String text, List<String> img_urls) {
+        log.info("创建Ai服务任务...{}", auditTaskUrl);
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+
+        Map<String, Object> analyzeRequest = new HashedMap<>();
+        analyzeRequest.put("text", StringUtils.hasText(text) ? text : "");
+        analyzeRequest.put("img_urls", img_urls);
+
+        HttpEntity<Map<String, Object>> analyzeEntity = new HttpEntity<>(analyzeRequest, analyzeHeaders);
+
+        ResponseEntity<String> analyzeResp = null;
+        try {
+            analyzeResp = restTemplate.postForEntity(auditTaskUrl, analyzeEntity, String.class);
+        } catch (org.springframework.web.client.HttpServerErrorException.ServiceUnavailable e) {
+            log.error("调用提交商品审核任务接口返回503 Service Unavailable错误: {}", e.getResponseBodyAsString());
+            return  null;
+        } catch (Exception e) {
+            log.error("调用提交商品审核任务接口异常", e);
+            return  null;
+        }
+
+        if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
+            String analyzeBody = analyzeResp.getBody();
+            log.info("提交商品审核任务提交成功, 返回: {}", analyzeBody);
+
+            JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+            JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+            if (dataJsonObj == null) {
+                log.error("提交商品审核任务返回数据为空");
+                R.fail("提交商品审核任务返回数据为空");
+                return  null;
+            }
+
+            // 获取record_id用于后续查询
+            String taskId = dataJsonObj.getString("task_id");
+            if (taskId == null) {
+                log.error("提交商品审核任务返回record_id为空");
+                R.fail("提交商品审核任务返回record_id为空");
+                return  null;
+            }
+            return taskId;
+        } else {
+            if (analyzeResp != null) {
+                log.error("调用提交商品审核任务接口失败, http状态: {}", analyzeResp.getStatusCode());
+                R.fail("调用提交商品审核任务接口失败, http状态: " + analyzeResp.getStatusCode());
+                return  null;
+            }
+        }
+        return  null;
+    }
+}

+ 272 - 0
alien-branch-second/src/main/java/shop/alien/second/util/AiUserViolationUtils.java

@@ -0,0 +1,272 @@
+package shop.alien.second.util;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.map.HashedMap;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeMessage;
+import shop.alien.entity.store.LifeUserViolation;
+import shop.alien.entity.store.vo.LifeUserVo;
+import shop.alien.mapper.LifeMessageMapper;
+import shop.alien.mapper.LifeUserMapper;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AiUserViolationUtils {
+
+    private final RestTemplate restTemplate;
+    private final LifeUserMapper lifeUserMapper;
+    private final LifeMessageMapper lifeMessageMapper;
+
+    @Value("${third-party-login.base-url:http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login}")
+    private String loginUrl;
+
+    @Value("${third-party-user-name.base-url:UdUser}")
+    private String userName;
+
+    @Value("${third-party-pass-word.base-url:123456}")
+    private String passWord;
+
+    @Value("${third-party-userComplaintRecordUrl.base-url:http://192.168.2.250:9000/ai/auto-review/api/v1/user_complaint_record/submit}")
+    private String userComplaintRecordUrl;
+
+
+    /**
+     * 登录 AI 服务,获取 token
+     *
+     * @return accessToken
+     */
+    public String getAccessToken() {
+        log.info("登录Ai服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        ResponseEntity<String> response;
+        try {
+            log.info("请求Ai服务登录接口===================>");
+            response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+        } catch (Exception e) {
+            log.error("请求AI服务登录接口失败", e);
+            return null;
+        }
+
+        if (response != null && response.getStatusCode() == HttpStatus.OK) {
+            String body = response.getBody();
+            log.info("请求Ai服务登录成功 postForEntity.getBody()\t{}", body);
+            if (StringUtils.hasText(body)) {
+                JSONObject jsonObject = JSONObject.parseObject(body);
+                if (jsonObject != null) {
+                    JSONObject dataJson = jsonObject.getJSONObject("data");
+                    if (dataJson != null) {
+                        return dataJson.getString("access_token");
+                    }
+                }
+            }
+            log.warn("AI服务登录响应解析失败 body: {}", body);
+            return null;
+        }
+
+        log.error("请求AI服务 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        return null;
+    }
+
+
+    /**
+     * 调用AI服务创建任务
+     *
+     * @return accessToken
+     */
+    public String createTask(String accessToken, LifeUserViolation lifeuserViolation) {
+        log.info("创建Ai服务任务...{}", userComplaintRecordUrl);
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+
+        Map<String, Object> analyzeRequest = new HashedMap<>();
+        // 对应参数
+        if (1 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "用户违规");
+        } else if (2 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "色情低俗");
+        } else if (3 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "违法违规");
+        } else if (4 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "谩骂嘲讽、煽动对立");
+        } else if (5 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "涉嫌诈骗");
+        } else if (6 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "人身攻击");
+        } else if (7 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "种族歧视");
+        } else if (8 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "政治敏感");
+        } else if (9 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "虚假、不实内容");
+        } else if (10 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "违反公德秩序");
+        } else if (11 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "危害人身安全");
+        } else if (12 == lifeuserViolation.getDictId()){
+            analyzeRequest.put("complaint_type", "网络暴力");
+        } else {
+            analyzeRequest.put("complaint_type", "其他");
+        }
+
+        analyzeRequest.put("reporter_user_id", lifeuserViolation.getReportingUserId());
+        analyzeRequest.put("reported_user_id", lifeuserViolation.getReportedUserId());
+
+        //举报人
+        String reporterUserId = lifeuserViolation.getReportingUserId();
+        //被举报人
+        String reportedUserId = lifeuserViolation.getReportedUserId();
+
+        //通过双方userId查询用户信息,查出用户电话,
+        QueryWrapper<LifeUserVo> reporterUserQueryWrapper = new QueryWrapper<>();
+        reporterUserQueryWrapper.eq("id", reporterUserId);
+        LifeUserVo reporterUserById = lifeUserMapper.getUserById(reporterUserQueryWrapper);
+        QueryWrapper<LifeUserVo> reportedUserQueryWrapper = new QueryWrapper<>();
+        reportedUserQueryWrapper.eq("id", reportedUserId);
+        LifeUserVo reportedUserById = lifeUserMapper.getUserById(reportedUserQueryWrapper);
+        // 用user_去拼接,查询life_message表,获取双方两天记录
+        String userReporterUserById = "user_" + reporterUserById.getUserPhone();
+        String userReportedUserById = "user_" + reportedUserById.getUserPhone();
+
+        //将双方两天记录拼接成text,传入analyzeRequest
+        // 获取双方最近的聊天记录
+        List<LifeMessage> chatMessages = getRecentChatMessages(userReporterUserById, userReportedUserById);
+
+        // 将聊天记录转换为文本格式
+        String text = convertMessagesToText(chatMessages);
+        analyzeRequest.put("conversation_context", StringUtils.hasText(text) ? text : "");
+
+//        analyzeRequest.put("text", StringUtils.hasText(text) ? text : "");
+//        analyzeRequest.put("img_urls", img_urls);
+
+        HttpEntity<Map<String, Object>> analyzeEntity = new HttpEntity<>(analyzeRequest, analyzeHeaders);
+
+        ResponseEntity<String> analyzeResp = null;
+        try {
+            analyzeResp = restTemplate.postForEntity(userComplaintRecordUrl, analyzeEntity, String.class);
+        } catch (org.springframework.web.client.HttpServerErrorException.ServiceUnavailable e) {
+            log.error("调用提交用户投诉审核任务接口返回503 Service Unavailable错误: {}", e.getResponseBodyAsString());
+            return  null;
+        } catch (Exception e) {
+            log.error("调用提交用户投诉审核任务接口异常", e);
+            return  null;
+        }
+
+        if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
+            String analyzeBody = analyzeResp.getBody();
+            log.info("提交用户投诉审核任务提交成功, 返回: {}", analyzeBody);
+
+            JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+            JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+            if (dataJsonObj == null) {
+                log.error("提交用户投诉审核任务返回数据为空");
+                R.fail("提交用户投诉审核任务返回数据为空");
+                return  null;
+            }
+
+            // 获取record_id用于后续查询
+            String taskId = dataJsonObj.getString("task_id");
+            if (taskId == null) {
+                log.error("提交用户投诉审核任务返回record_id为空");
+                R.fail("提交用户投诉审核任务返回record_id为空");
+                return  null;
+            }
+            return taskId;
+        } else {
+            if (analyzeResp != null) {
+                log.error("调用提交用户投诉审核任务接口失败, http状态: {}", analyzeResp.getStatusCode());
+                R.fail("调用提交用户投诉审核任务接口失败, http状态: " + analyzeResp.getStatusCode());
+                return  null;
+            }
+        }
+        return  null;
+    }
+
+    /**
+     * 获取双方用户的最近聊天记录
+     * @param userReporterUserById 举报人ID
+     * @param userReportedUserById 被举报人ID
+     * @return 聊天记录列表
+     */
+    public List<LifeMessage> getRecentChatMessages(String userReporterUserById, String userReportedUserById) {
+        // 构建查询条件
+        QueryWrapper<LifeMessage> queryWrapper = new QueryWrapper<>();
+
+        // 查询双方的聊天记录
+        queryWrapper.and(wrapper -> wrapper
+                        .eq("sender_id", userReporterUserById)
+                        .eq("receiver_id", userReportedUserById))
+                .or(wrapper -> wrapper
+                        .eq("sender_id", userReportedUserById)
+                        .eq("receiver_id", userReporterUserById));
+
+        // 按时间倒序排列
+        queryWrapper.orderByDesc("created_time");
+
+        // 限制50条记录
+        queryWrapper.last("LIMIT 50");
+
+        return lifeMessageMapper.selectList(queryWrapper);
+    }
+
+    /**
+     * 将聊天消息列表转换为文本格式
+     * @param messages 聊天消息列表
+     * @return 格式化后的文本字符串
+     */
+    private String convertMessagesToText(List<LifeMessage> messages) {
+        if (messages == null || messages.isEmpty()) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        // 按时间顺序排列(因为数据库查询是倒序的,这里需要重新排序)
+        for (int i = messages.size() - 1; i >= 0; i--) {
+            LifeMessage message = messages.get(i);
+            // 格式:[时间] 发送者ID: 消息内容
+            sb.append("[").append(formatTime(message.getCreatedTime())).append("] ")
+                    .append(message.getSenderId()).append(": ")
+                    .append(message.getContent()).append("\n");
+        }
+
+        return sb.toString().trim();
+    }
+
+    /**
+     * 格式化时间显示
+     * @param date 时间
+     * @return 格式化后的时间字符串
+     */
+    private String formatTime(Date date) {
+        if (date == null) {
+            return "";
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        return sdf.format(date);
+    }
+}

+ 91 - 0
alien-branch-second/src/main/java/shop/alien/second/util/JsonUtils.java

@@ -0,0 +1,91 @@
+package shop.alien.second.util;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JsonUtils {
+    private static final ObjectMapper objectMapper = new ObjectMapper();
+
+    /**
+     * 从JSON字符串中获取指定key的值
+     */
+    public static String getJsonValue(String jsonString, String key) {
+        try {
+            JsonNode jsonNode = objectMapper.readTree(jsonString);
+            JsonNode valueNode = jsonNode.get(key);
+            return valueNode != null ? valueNode.asText() : null;
+        } catch (Exception e) {
+            throw new RuntimeException("解析JSON失败", e);
+        }
+    }
+
+    /**
+     * 获取指定key的值(支持各种数据类型)
+     */
+    public static <T> T getJsonValue(String jsonString, String key, Class<T> valueType) {
+        try {
+            JsonNode jsonNode = objectMapper.readTree(jsonString);
+            JsonNode valueNode = jsonNode.get(key);
+            return objectMapper.treeToValue(valueNode, valueType);
+        } catch (Exception e) {
+            throw new RuntimeException("解析JSON失败", e);
+        }
+    }
+
+
+    /**
+     * 从图片对象中获取URL(支持多种可能的字段名)
+     * 例如:从 banner_image 或 vertical_image 对象中获取 url 或 imgUrl
+     *
+     * @param jsonString JSON字符串
+     * @param imageObjectKey 图片对象的key,如 "banner_image" 或 "vertical_image"
+     * @return 图片URL字符串,如果不存在则返回null
+     */
+    public static String getImageUrlFromObject(String jsonString, String imageObjectKey) {
+        try {
+            JsonNode jsonNode = objectMapper.readTree(jsonString);
+            JsonNode dataNode = jsonNode.get("data");
+            if (dataNode == null) {
+                return null;
+            }
+            
+            JsonNode imageNode = dataNode.get(imageObjectKey);
+            if (imageNode == null || !imageNode.isObject()) {
+                return null;
+            }
+            
+            // 尝试常见的URL字段名
+            String[] possibleUrlFields = {"url", "imgUrl", "imageUrl", "img_url", "image_url"};
+            for (String field : possibleUrlFields) {
+                JsonNode urlNode = imageNode.get(field);
+                if (urlNode != null && urlNode.isTextual()) {
+                    return urlNode.asText();
+                }
+            }
+            
+            return null;
+        } catch (Exception e) {
+            throw new RuntimeException("解析图片URL失败,对象key: " + imageObjectKey, e);
+        }
+    }
+
+    /**
+     * 获取banner图片URL
+     *
+     * @param jsonString JSON字符串
+     * @return banner图片URL
+     */
+    public static String getBannerImageUrl(String jsonString) {
+        return getImageUrlFromObject(jsonString, "banner_image");
+    }
+
+    /**
+     * 获取竖版图片URL
+     *
+     * @param jsonString JSON字符串
+     * @return 竖版图片URL
+     */
+    public static String getVerticalImageUrl(String jsonString) {
+        return getImageUrlFromObject(jsonString, "vertical_image");
+    }
+}

+ 20 - 0
alien-branch-second/src/main/resources/bootstrap-dev.yml

@@ -0,0 +1,20 @@
+spring:
+  application:
+    name: alien-second
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: 192.168.2.252:8848
+        username: nacos
+        password: ngfriend198092
+
+      #配置中心
+      config:
+        enabled: true
+        server-addr: 192.168.2.252:8848
+        username: nacos
+        password: ngfriend198092
+        group: DEFAULT_GROUP
+        file-extension: yml

+ 22 - 0
alien-branch-second/src/main/resources/bootstrap-prod.yml

@@ -0,0 +1,22 @@
+spring:
+  application:
+    name: alien-second
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: localhost:8848
+        username: nacos
+        password: ngfriend198092
+        namespace: 3cbb802a-b56e-47f7-b658-b5012ecafb1f
+
+      #配置中心
+      config:
+        enabled: true
+        server-addr: localhost:8848
+        username: nacos
+        password: ngfriend198092
+        group: DEFAULT_GROUP
+        file-extension: yml
+        namespace: 3cbb802a-b56e-47f7-b658-b5012ecafb1f

+ 22 - 0
alien-branch-second/src/main/resources/bootstrap-test.yml

@@ -0,0 +1,22 @@
+spring:
+  application:
+    name: alien-second
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: 192.168.2.252:8848
+        username: nacos
+        password: ngfriend198092
+        namespace: 0e1e2d77-56e8-422c-8317-6f71d7285e59
+
+      #配置中心
+      config:
+        enabled: true
+        server-addr: 192.168.2.252:8848
+        username: nacos
+        password: ngfriend198092
+        group: DEFAULT_GROUP
+        file-extension: yml
+        namespace: 0e1e2d77-56e8-422c-8317-6f71d7285e59

+ 22 - 0
alien-branch-second/src/main/resources/bootstrap-uat.yml

@@ -0,0 +1,22 @@
+spring:
+  application:
+    name: alien-second
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: localhost:8848
+        username: nacos
+        password: ngfriend198092
+        namespace: 79060c39-10ad-4098-9022-5e8a47796f8f
+
+      #配置中心
+      config:
+        enabled: true
+        server-addr: localhost:8848
+        username: nacos
+        password: ngfriend198092
+        group: DEFAULT_GROUP
+        file-extension: yml
+        namespace: 79060c39-10ad-4098-9022-5e8a47796f8f

+ 3 - 0
alien-branch-second/src/main/resources/bootstrap.yml

@@ -0,0 +1,3 @@
+spring:
+  profiles:
+    active: test

+ 173 - 0
alien-branch-second/src/main/resources/logback-spring.xml

@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
+<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
+<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
+<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
+<!-- 该信息是由于设置了当配置文件变化时重新加载,所以每当达到扫描时间的时候就会检查配置文件是否错误。但是由于一般配置文件都放在了JAR包中,
+    而扫描的时候无法扫描JAR包内,因此会提示没有可以检查的文件,所以每隔一段时间就输出一次-->
+<configuration scan="false" scanPeriod="60 seconds" debug="true">
+    <contextName>logback-spring</contextName>
+
+    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使“${}”来使用变量。 -->
+    <!-- 定义全局参数常量 -->
+    <property name="log.level" value="debug"/>
+    <property name="log.maxHistory" value="30"/><!-- 30表示30个 -->
+    <springProperty scope="context" name="logging.path" source="logging.path"/>
+    <!--输出文件前缀-->
+    <property name="FILENAME" value="alien"/>
+
+    <!--0. 日志格式和颜色渲染 -->
+    <!-- 彩色日志依赖的渲染类 -->
+    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
+    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
+    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
+
+    <!-- 文件输出格式 -->
+    <property name="FILE_LOG_PATTERN" value="[%d{MM/dd HH:mm:ss.SSS}][%-10.10thread][%-5level][%-40.40c{1}:%5line]:[%15method] || %m%n"/>
+    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+
+    <!--1. 输出到控制台-->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>${log.level}</level>
+        </filter>
+        <encoder>
+            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+            <!-- 设置字符集 -->
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <!--2. 输出到文档-->
+    <!-- DEBUG 日志 -->
+    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 当前的日志文件存放路径 -->
+        <file>${logging.path}/DEBUG.log</file>
+        <!-- 日志滚动策略 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 历史日志文件的存放路径和名称 -->
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_DEBUG.log.gz</fileNamePattern>
+            <!-- 日志文件最大的保存历史 数量-->
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <!--日志文件最大的大小-->
+        <!--        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">-->
+        <!--            <MaxFileSize>10MB</MaxFileSize>-->
+        <!--        </triggeringPolicy>-->
+        <!-- 此日志文档只记录debug级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>DEBUG</level>
+            <onMatch>ACCEPT</onMatch>  <!-- 用过滤器,只接受DEBUG级别的日志信息,其余全部过滤掉 -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- INFO 日志 -->
+    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logging.path}/INFO.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_INFO.log.gz</fileNamePattern>
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- WARN 日志 -->
+    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logging.path}/WARN.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_WARN.log.gz</fileNamePattern>
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>WARN</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logging.path}/ERROR.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_ERROR.log.gz</fileNamePattern>
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--
+      <logger>用来设置某一个包或者具体的某一个类的日志打印级别、
+      以及指定<appender>。<logger>仅有一个name属性,
+      一个可选的level和一个可选的addtivity属性。
+      name:用来指定受此logger约束的某一个包或者具体的某一个类。
+      level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+         还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
+         如果未设置此属性,那么当前logger将会继承上级的级别。
+      addtivity:是否向上级logger传递打印信息。默认是true。
+      <logger name="org.springframework.web" level="info"/>
+      <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
+    -->
+
+    <!--
+      使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
+      第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
+      第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
+      【logging.level.org.mybatis=debug logging.level.dao=debug】
+     -->
+    <!-- mybatis显示sql,修改此处扫描包名 -->
+
+
+    <!--
+      root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
+      level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+      不能设置为INHERITED或者同义词NULL。默认是DEBUG
+      可以包含零个或多个元素,标识这个appender将会添加到这个logger。
+    -->
+
+    <!-- 4. 最终的策略 -->
+    <!-- 4.1 开发环境:打印控制台-->
+    <!--打印sql-->
+    <!--    <logger name="com.veryhappy.music.dao" level="debug"/>-->
+
+    <!--打印log-->
+    <root level="info">
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="DEBUG_FILE"/>
+        <appender-ref ref="INFO_FILE"/>
+        <appender-ref ref="WARN_FILE"/>
+        <appender-ref ref="ERROR_FILE"/>
+    </root>
+
+    <!--   4.2 生产环境:输出到文档-->
+    <springProfile name="pro">
+        <root level="info">
+            <appender-ref ref="CONSOLE"/>
+            <appender-ref ref="DEBUG_FILE"/>
+            <appender-ref ref="INFO_FILE"/>
+            <appender-ref ref="ERROR_FILE"/>
+            <appender-ref ref="WARN_FILE"/>
+        </root>
+    </springProfile>
+</configuration>

+ 66 - 0
alien-service-config/pom.xml

@@ -20,4 +20,70 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
 
+    <dependencies>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.github.openfeign</groupId>
+            <artifactId>feign-okhttp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-extension</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-util</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>8</source>
+                    <target>8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>

+ 66 - 0
alien-service-config/src/main/java/shop/alien/config/databases/DruidConfig.java

@@ -0,0 +1,66 @@
+package shop.alien.config.databases;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.support.http.StatViewServlet;
+import com.alibaba.druid.support.http.WebStatFilter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.sql.DataSource;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Druid
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2023/8/29 17:41
+ */
+@Configuration
+public class DruidConfig {
+
+    @ConfigurationProperties(prefix = "spring.datasource.druid")
+    @Bean
+    public DataSource druid() {
+        return new DruidDataSource();
+    }
+
+    /**
+     * 配置一个管理后台的Servlet
+     *
+     * @return
+     */
+    @Bean
+    public ServletRegistrationBean statViewServlet() {
+        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
+        Map<String, String> initParams = new HashMap<>();
+        initParams.put("loginUsername", "admin");
+        initParams.put("loginPassword", ".,ZyL021405");
+        initParams.put("allow", "");//默认就是允许所有访问
+        //initParams.put("deny","127.0.0.1");
+        bean.setInitParameters(initParams);
+        return bean;
+    }
+
+    /**
+     * 配置一个web监控的filter
+     *
+     * @return
+     */
+    @Bean
+    public FilterRegistrationBean webStatFilter() {
+        FilterRegistrationBean bean = new FilterRegistrationBean();
+        bean.setFilter(new WebStatFilter());
+        Map<String, String> initParams = new HashMap<>();
+        initParams.put("exclusions", "*.js,*.css,/druid/*");
+        bean.setInitParameters(initParams);
+        bean.setUrlPatterns(Arrays.asList("/*"));
+        return bean;
+    }
+
+}

+ 63 - 0
alien-service-config/src/main/java/shop/alien/config/databases/MyBatisFieldHandler.java

@@ -0,0 +1,63 @@
+package shop.alien.config.databases;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import shop.alien.util.common.JwtUtil;
+
+import java.util.Date;
+import java.util.Objects;
+
+/**
+ * Mybatis日期填充
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/6 10:19
+ */
+@Slf4j
+@Component
+public class MyBatisFieldHandler implements MetaObjectHandler {
+
+    /**
+     * insert操作时填充方法
+     *
+     * @param metaObject 元对象
+     */
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        log.info("=================================insertFill=========================================");
+        System.out.println(metaObject.getOriginalObject());
+        //字段为实体类名, 不是表字段名
+        this.setFieldValByName("createdTime", new Date(), metaObject);
+        this.setFieldValByName("updatedTime", new Date(), metaObject);
+        if (JwtUtil.hasToken()) {
+            this.setFieldValByName("createdUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
+            this.setFieldValByName("updatedUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
+        } else {
+            this.setFieldValByName("createdUserId", 0, metaObject);
+            this.setFieldValByName("updatedUserId", 0, metaObject);
+        }
+    }
+
+    /**
+     * update操作时填充方法
+     *
+     * @param metaObject 元对象
+     */
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        log.info("=================================updateFill=========================================");
+        //字段为实体类名, 不是表字段名
+        this.setFieldValByName("updatedTime", new Date(), metaObject);
+        if (JwtUtil.hasToken()) {
+            this.setFieldValByName("updatedUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
+        } else {
+            this.setFieldValByName("updatedUserId", 0, metaObject);
+        }
+    }
+}

+ 16 - 0
alien-service-config/src/main/java/shop/alien/config/databases/MyBatisPlusPageConfig.java

@@ -0,0 +1,16 @@
+package shop.alien.config.databases;
+
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * MybatisPlus分页
+ */
+@Configuration
+public class MyBatisPlusPageConfig {
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        return new PaginationInterceptor();
+    }
+}

+ 64 - 0
alien-service-config/src/main/java/shop/alien/config/feign/FeignBodyLogger.java

@@ -0,0 +1,64 @@
+package shop.alien.config.feign;
+
+import feign.Logger;
+import feign.Request;
+import feign.Response;
+import feign.Util;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+
+/**
+ * 实现 Feign 日志拦截器
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2025/3/19 9:51
+ */
+@Slf4j
+public class FeignBodyLogger extends Logger {
+
+    @Override
+    protected void logRequest(String configKey, Level logLevel, Request request) {
+        // 可选:打印请求日志
+    }
+
+    @Override
+    protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime) throws IOException {
+        // 打印状态码和头信息
+        log.info("状态码: {}", response.status());
+        log.info("响应头: {}", response.headers());
+        if (!isGzipResponse(response)) {
+            // 读取并打印响应体(已自动解压)
+            if (response.body() != null && !(response.status() == 204 || response.status() == 205)) {
+                byte[] bodyData = Util.toByteArray(response.body().asInputStream());
+                String body = new String(bodyData, StandardCharsets.UTF_8);
+                log.info("响应体: {}", body);
+                // 重建响应对象避免流关闭
+                return response.toBuilder().body(bodyData).build();
+            }
+        }
+        return response;
+    }
+
+    @Override
+    protected void log(String configKey, String format, Object... args) {
+        // 默认日志输出(可选)
+    }
+
+    /**
+     * 判断是否为 GZip 压缩响应
+     *
+     * @param response 请求内容
+     * @return 是否为 GZip 压缩响应
+     */
+    private boolean isGzipResponse(Response response) {
+        Collection<String> strings = response.headers().get("Content-Encoding");
+        if (null == strings) {
+            return false;
+        }
+        return response.headers().get("Content-Encoding").stream().anyMatch(enc -> enc.contains("gzip"));
+    }
+}

+ 26 - 0
alien-service-config/src/main/java/shop/alien/config/feign/FeignNacosConfig.java

@@ -0,0 +1,26 @@
+package shop.alien.config.feign;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Component;
+
+/**
+ * 从 Nacos读取Feign配置
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2025/3/24 14:18
+ */
+@Data
+@Component
+@RefreshScope
+public class FeignNacosConfig {
+
+    @Value("${openfeign.timeout.connect}")
+    private int connectTimeout;
+
+    @Value("${openfeign.timeout.read}")
+    private int readTimeout;
+
+}

+ 58 - 0
alien-service-config/src/main/java/shop/alien/config/feign/FeignOptionConfig.java

@@ -0,0 +1,58 @@
+package shop.alien.config.feign;
+
+import feign.Logger;
+import feign.Request;
+import feign.codec.Decoder;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.cloud.openfeign.support.SpringDecoder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 从 Openfeign配置
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/10/21 19:52
+ */
+@Configuration
+@RequiredArgsConstructor
+public class FeignOptionConfig {
+
+    private final FeignNacosConfig feignNacosConfig;
+
+    @Bean
+    public Request.Options options() {
+        return new Request.Options(feignNacosConfig.getConnectTimeout(), feignNacosConfig.getReadTimeout(), true);
+    }
+
+    /**
+     * OpenFeign打印日志, 需在配置文件中指定feign类位置
+     * <p>
+     * NONE, 默认,不显示任何日志
+     * BASIC, 仅记录请求方法、url、响应状态码及执行时间
+     * HEADERS, 除记录BASIC信息外,还记录请求头和响应头
+     * FULL,除了HEADERS信息外,还有请求和响应正文以及元数据
+     *
+     * @return 日志等级
+     */
+    @Bean
+    Logger.Level feignLoggerLevel() {
+        return Logger.Level.BASIC;
+//        return Logger.Level.FULL; // 必须设置为 FULL 才能获取响应体
+    }
+
+//    @Bean
+//    public FeignBodyLogger feignLogger() {
+//        return new FeignBodyLogger();
+//    }
+
+    @Bean
+    public Decoder feignDecoder(ObjectFactory<HttpMessageConverters> converters) {
+        return new GzipResponseDecoder(new SpringDecoder(converters));
+    }
+
+
+}

+ 75 - 0
alien-service-config/src/main/java/shop/alien/config/feign/GzipResponseDecoder.java

@@ -0,0 +1,75 @@
+package shop.alien.config.feign;
+
+import feign.Response;
+import feign.codec.Decoder;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * 实现支持 GZip 的解码器
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2025/3/19 9:47
+ */
+@Slf4j
+public class GzipResponseDecoder implements Decoder {
+
+    private final Decoder delegate;
+
+    public GzipResponseDecoder(Decoder delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public Object decode(Response response, Type type) throws IOException {
+        // 判断是否为 GZip 压缩响应
+        if (isGzipResponse(response)) {
+            try (InputStream is = response.body().asInputStream();
+                 GZIPInputStream gzipIs = new GZIPInputStream(is)) {
+                // 读取解压后的数据
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                byte[] buffer = new byte[1024];
+                int len;
+                while ((len = gzipIs.read(buffer)) > 0) {
+                    baos.write(buffer, 0, len);
+                }
+//                // 打印解压后的原始内容
+//                String rawBody = baos.toString("UTF-8");
+//                System.out.println("[GZip 解压内容] " + rawBody);
+                // 构造新的响应对象供后续解析
+                Response modifiedResponse = Response.builder()
+                        .body(baos.toByteArray())
+                        .headers(response.headers())
+                        .status(response.status())
+                        .request(response.request())
+                        .build();
+                return delegate.decode(modifiedResponse, type);
+            }
+        }
+        return delegate.decode(response, type);
+    }
+
+    /**
+     * 判断是否为 GZip 压缩响应
+     *
+     * @param response 请求内容
+     * @return 是否为 GZip 压缩响应
+     */
+    private boolean isGzipResponse(Response response) {
+        Collection<String> strings = response.headers().get("Content-Encoding");
+        if (null == strings) {
+            return false;
+        }
+        return response.headers().get("Content-Encoding")
+                .stream()
+                .anyMatch(enc -> enc.contains("gzip"));
+    }
+
+}

+ 109 - 0
alien-service-config/src/main/java/shop/alien/config/feign/OkHttpConfig.java

@@ -0,0 +1,109 @@
+package shop.alien.config.feign;
+
+import okhttp3.ConnectionPool;
+import okhttp3.OkHttpClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.net.ssl.*;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * OkHttp配置
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/10/27 10:29
+ */
+@Configuration
+public class OkHttpConfig {
+
+    /**
+     * OkHttp 客户端配置
+     *
+     * @return OkHttp 客户端配
+     */
+    @Bean
+    public OkHttpClient okHttpClient() {
+        return new OkHttpClient.Builder()
+                .sslSocketFactory(sslSocketFactory(), x509TrustManager())
+                .hostnameVerifier(hostnameVerifier())
+                //是否开启缓存
+                .retryOnConnectionFailure(false)
+                //连接池
+                .connectionPool(pool())
+                //连接超时时间
+                .connectTimeout(15L, TimeUnit.SECONDS)
+                //读取超时时间
+                .readTimeout(15L, TimeUnit.SECONDS)
+                //是否允许重定向
+                .followRedirects(true)
+                .build();
+    }
+
+    /**
+     * 忽略证书校验
+     *
+     * @return 证书信任管理器
+     */
+    @Bean
+    public X509TrustManager x509TrustManager() {
+        return new X509TrustManager() {
+            @Override
+            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
+            }
+
+            @Override
+            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
+            }
+
+            @Override
+            public X509Certificate[] getAcceptedIssuers() {
+                return new X509Certificate[0];
+            }
+        };
+    }
+
+    /**
+     * 信任所有 SSL 证书
+     *
+     * @return
+     */
+    @Bean
+    public SSLSocketFactory sslSocketFactory() {
+        try {
+            TrustManager[] trustManagers = new TrustManager[]{x509TrustManager()};
+            SSLContext sslContext = SSLContext.getInstance("SSL");
+            sslContext.init(null, trustManagers, new SecureRandom());
+            return sslContext.getSocketFactory();
+        } catch (NoSuchAlgorithmException | KeyManagementException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 连接池配置
+     *
+     * @return 连接池
+     */
+    @Bean
+    public ConnectionPool pool() {
+        // 最大连接数、连接存活时间、存活时间单位(分钟)
+        return new ConnectionPool(200, 5, TimeUnit.MINUTES);
+    }
+
+    /**
+     * 信任所有主机名
+     *
+     * @return 主机名校验
+     */
+    @Bean
+    public HostnameVerifier hostnameVerifier() {
+        return (s, sslSession) -> true;
+    }
+}

+ 34 - 0
alien-service-config/src/main/java/shop/alien/config/filter/FilterConfig.java

@@ -0,0 +1,34 @@
+package shop.alien.config.filter;
+
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.servlet.Filter;
+
+/**
+ * 过滤器配置类
+ **/
+@Configuration
+public class FilterConfig {
+
+    /**
+     * 注册过滤器
+     */
+    @Bean
+    public FilterRegistrationBean someFilterRegistration() {
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        registration.setFilter(replaceStreamFilter());
+        registration.addUrlPatterns("/*");
+        registration.setName("streamFilter");
+        return registration;
+    }
+
+    /**
+     * 实例化StreamFilter
+     */
+    @Bean(name = "replaceStreamFilter")
+    public Filter replaceStreamFilter() {
+        return new ReplaceStreamFilter();
+    }
+}

+ 23 - 0
alien-service-config/src/main/java/shop/alien/config/filter/HttpRequestConfig.java

@@ -0,0 +1,23 @@
+package shop.alien.config.filter;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * 可以集中在这里配置拦截器、过滤器、静态资源缓存等
+ */
+@Configuration
+public class HttpRequestConfig implements WebMvcConfigurer {
+
+    @Bean
+    public RequestInterceptor getSignatureInterceptor() {
+        return new RequestInterceptor();
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(getSignatureInterceptor()).addPathPatterns("/**");
+    }
+}

+ 38 - 0
alien-service-config/src/main/java/shop/alien/config/filter/NoRepeatSubmit.java

@@ -0,0 +1,38 @@
+package shop.alien.config.filter;
+
+import java.lang.annotation.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 防止重复提交注解
+ * 
+ * @author lingma
+ * @version 1.0
+ */
+@Inherited
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface NoRepeatSubmit {
+    
+    /**
+     * 锁过期时间,默认5秒
+     * 
+     * @return 过期时间
+     */
+    int expireTime() default 5;
+    
+    /**
+     * 时间单位,默认为秒
+     * 
+     * @return 时间单位
+     */
+    TimeUnit timeUnit() default TimeUnit.SECONDS;
+    
+    /**
+     * 错误提示信息
+     * 
+     * @return 提示信息
+     */
+    String message() default "请勿重复提交";
+}

+ 25 - 0
alien-service-config/src/main/java/shop/alien/config/filter/NoRepeatSubmitConfig.java

@@ -0,0 +1,25 @@
+package shop.alien.config.filter;
+
+import lombok.Setter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * 防重复提交配置类
+ * 
+ * @author lingma
+ * @version 1.0
+ */
+@Setter
+@Configuration
+public class NoRepeatSubmitConfig implements WebMvcConfigurer {
+    
+    private NoRepeatSubmitInterceptor noRepeatSubmitInterceptor;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(noRepeatSubmitInterceptor)
+                .addPathPatterns("/**");
+    }
+}

+ 117 - 0
alien-service-config/src/main/java/shop/alien/config/filter/NoRepeatSubmitInterceptor.java

@@ -0,0 +1,117 @@
+package shop.alien.config.filter;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.util.DigestUtils;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import shop.alien.config.redis.BaseRedisService;
+import shop.alien.entity.result.R;
+import shop.alien.util.common.JwtUtil;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 防止重复提交拦截器
+ * 
+ * @author lingma
+ * @version 1.0
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class NoRepeatSubmitInterceptor implements HandlerInterceptor {
+    
+    private final BaseRedisService baseRedisService;
+    
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        if (handler instanceof HandlerMethod) {
+            HandlerMethod handlerMethod = (HandlerMethod) handler;
+            Method method = handlerMethod.getMethod();
+            NoRepeatSubmit noRepeatSubmit = method.getAnnotation(NoRepeatSubmit.class);
+            
+            if (noRepeatSubmit != null) {
+                String key = generateKey(request, method);
+                if (!lock(key, noRepeatSubmit)) {
+                    // 重复提交
+                    handleRepeatSubmit(response, noRepeatSubmit.message());
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+    
+    /**
+     * 生成锁的键值
+     * key 值组成: repeat_submit:方法所在类名:方法名:用户ID(如果已登录)或IP地址:请求URI的MD5摘
+     * @param request 请求
+     * @param method  方法
+     * @return 键值
+     */
+    private String generateKey(HttpServletRequest request, Method method) {
+        StringBuilder sb = new StringBuilder("repeat_submit:");
+        sb.append(method.getDeclaringClass().getName());
+        sb.append(":");
+        sb.append(method.getName());
+        sb.append(":");
+        
+        // 添加用户标识
+        JSONObject userInfo = JwtUtil.getCurrentUserInfo();
+        if (userInfo != null) {
+            sb.append(userInfo.getInteger("userId"));
+        } else {
+            sb.append(request.getRemoteAddr());
+        }
+        
+        // 添加请求参数
+        sb.append(":");
+        sb.append(DigestUtils.md5DigestAsHex(request.getRequestURI().getBytes()));
+        
+        return sb.toString();
+    }
+    
+    /**
+     * 加锁 分布式锁实现
+     * 如果键不存在则设置成功并返回 true,否则返回 false
+     * @param key 键
+     * @param noRepeatSubmit 注解
+     * @return 是否加锁成功
+     */
+    private boolean lock(String key, NoRepeatSubmit noRepeatSubmit) {
+        try {
+            // 使用Redis的SET命令的NX和EX选项实现分布式锁
+            // 键的过期时间由 @NoRepeatSubmit 注解中的参数决定
+            Boolean result = baseRedisService.setStringIfAbsent(key, "1", 
+                noRepeatSubmit.timeUnit().toSeconds(noRepeatSubmit.expireTime()));
+            return result != null && result;
+        } catch (Exception e) {
+            log.error("防重复提交加锁异常", e);
+            return true; // 出现异常不阻止用户操作
+        }
+    }
+    
+    /**
+     * 处理重复提交
+     * 
+     * @param response 响应
+     * @param message 提示信息
+     * @throws IOException IO异常
+     */
+    private void handleRepeatSubmit(HttpServletResponse response, String message) throws IOException {
+        response.setContentType("application/json;charset=UTF-8");
+        PrintWriter writer = response.getWriter();
+        R<Object> fail = R.fail(message);
+        writer.write(JSONObject.toJSONString(fail));
+        writer.flush();
+        writer.close();
+    }
+}

+ 21 - 0
alien-service-config/src/main/java/shop/alien/config/filter/ReplaceStreamFilter.java

@@ -0,0 +1,21 @@
+package shop.alien.config.filter;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * 替换HttpServletRequest
+ **/
+@Slf4j
+public class ReplaceStreamFilter implements Filter {
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);
+        chain.doFilter(requestWrapper, response);
+    }
+
+}

+ 75 - 0
alien-service-config/src/main/java/shop/alien/config/filter/RequestInterceptor.java

@@ -0,0 +1,75 @@
+package shop.alien.config.filter;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.http.MediaType;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 编写拦截器
+ **/
+@Slf4j
+public class RequestInterceptor implements HandlerInterceptor {
+
+    private Environment environment;
+
+    @Autowired
+    public void setEnvironment(Environment environment) {
+        this.environment = environment;
+    }
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+        StringBuffer requestURL = request.getRequestURL();
+        String serviceName = environment.getProperty("server.name");
+        if (requestURL.indexOf(serviceName) > -1) {
+            if (isJson(request)) {
+                log.info("--------------Post请求--------------");
+                log.info("请求地址: {}", requestURL);
+                // 获取json字符串
+                try {
+                    String jsonParam = new RequestWrapper(request).getBodyString();
+                    log.info("请求参数: {}", jsonParam);
+                } catch (Exception e) {
+                    log.error("请求参数获取异常: {}", e.getMessage());
+                }
+            } else {
+                log.info("--------------Get请求--------------");
+                log.info("请求地址: {}", requestURL);
+                //获取请求参数
+                String queryString = request.getQueryString();
+                if (queryString != null) {
+                    log.info("请求参数: {}", queryString);
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
+    }
+
+    /**
+     * 判断本次请求的数据类型是否为json
+     */
+    private boolean isJson(HttpServletRequest request) {
+        //判断form-data, application/json, application/json;charset=UTF-8
+        if (request.getContentType() != null) {
+            return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) ||
+                    request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE) ||
+                    request.getContentType().equals(MediaType.MULTIPART_FORM_DATA_VALUE) ||
+                    request.getContentType().equals("application/json; charset=UTF-8");
+        }
+        return false;
+    }
+}

+ 105 - 0
alien-service-config/src/main/java/shop/alien/config/filter/RequestWrapper.java

@@ -0,0 +1,105 @@
+package shop.alien.config.filter;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.*;
+import java.nio.charset.Charset;
+
+/**
+ * 包装HttpServletRequest,目的是让其输入流可重复读
+ **/
+@Slf4j
+public class RequestWrapper extends HttpServletRequestWrapper {
+
+    /**
+     * 存储body数据的容器
+     */
+    private final byte[] body;
+
+    public RequestWrapper(HttpServletRequest request) {
+        super(request);
+        // 将body数据存储起来
+        String bodyStr = getBodyString(request);
+        body = bodyStr.getBytes(Charset.defaultCharset());
+    }
+
+    /**
+     * 获取请求Body
+     */
+    public String getBodyString(final ServletRequest request) {
+        try {
+            return inputStream2String(request.getInputStream());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 获取请求Body
+     */
+    public String getBodyString() {
+        final InputStream inputStream = new ByteArrayInputStream(body);
+        return inputStream2String(inputStream);
+    }
+
+    /**
+     * 将inputStream里的数据读取出来并转换成字符串
+     */
+    private String inputStream2String(InputStream inputStream) {
+        StringBuilder sb = new StringBuilder();
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
+            String line;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                    log.error("", e);
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public BufferedReader getReader() {
+        return new BufferedReader(new InputStreamReader(getInputStream()));
+    }
+
+    @Override
+    public ServletInputStream getInputStream() {
+        final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);
+        return new ServletInputStream() {
+            @Override
+            public int read() {
+                return inputStream.read();
+            }
+
+            @Override
+            public boolean isFinished() {
+                return false;
+            }
+
+            @Override
+            public boolean isReady() {
+                return false;
+            }
+
+            @Override
+            public void setReadListener(ReadListener readListener) {
+            }
+        };
+    }
+}

+ 39 - 0
alien-service-config/src/main/java/shop/alien/config/http/HttpConfig.java

@@ -0,0 +1,39 @@
+package shop.alien.config.http;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2022/7/11 14:35
+ */
+@Configuration
+public class HttpConfig {
+
+    //单位为ms
+    private static final Integer READ_TIME_OUT = 50000;
+    private static final Integer CONNECTION_TIME_OUT = 50000;
+
+    /**
+     * restTemplate用于发送post请求
+     *
+     * @param factory ClientHttpRequestFactory
+     * @return RestTemplate
+     */
+    @Bean
+    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
+        return new RestTemplate(factory);
+    }
+
+    @Bean
+    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
+        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+        factory.setReadTimeout(READ_TIME_OUT);
+        factory.setConnectTimeout(CONNECTION_TIME_OUT);
+        return factory;
+    }
+}

+ 120 - 0
alien-service-config/src/main/java/shop/alien/config/properties/RiskControlProperties.java

@@ -0,0 +1,120 @@
+package shop.alien.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@RefreshScope
+@ConfigurationProperties(prefix = "risk-control")
+public class RiskControlProperties {
+
+    /**
+     * 洗钱嫌疑规则配置
+     */
+    private MoneyLaundering moneyLaundering = new MoneyLaundering();
+
+    /**
+     * 账号异常规则配置
+     */
+    private AccountAbnormal accountAbnormal = new AccountAbnormal();
+
+    /**
+     * 交易欺诈规则配置
+     */
+    private TradeFraud tradeFraud = new TradeFraud();
+
+    /**
+     * 异常发布规则配置
+     */
+    private AbnormalPublish abnormalPublish = new AbnormalPublish();
+
+    @Data
+    public static class MoneyLaundering {
+        /**
+         * 每天交易次数阈值
+         */
+        private int dailyCount = 5;
+
+        /**
+         * 每笔交易金额阈值(元)
+         */
+        private double amountThreshold = 200.0;
+
+        /**
+         * 风险类型
+         */
+        private String riskType = "洗钱嫌疑";
+
+        /**
+         * 检测指标
+         */
+        private String detectIndicator = "高频高价交易";
+    }
+
+    @Data
+    public static class AccountAbnormal {
+        /**
+         * 24小时内同一设备/mac注册账号数量阈值
+         */
+        private int regCount24h = 3;
+
+        /**
+         * 风险类型
+         */
+        private String riskType = "账号异常";
+
+        /**
+         * 检测指标
+         */
+        private String detectIndicator = "同一设备24小时内注册超过3个账号";
+    }
+
+    @Data
+    public static class TradeFraud {
+        /**
+         * 24小时内发布成功记录次数阈值
+         */
+        private int publishCount24h = 3;
+
+        /**
+         * 时间窗口(小时)
+         */
+        private int timeWindowHours = 24;
+
+        /**
+         * 风险类型
+         */
+        private String riskType = "交易欺诈";
+
+        /**
+         * 检测指标
+         */
+        private String detectIndicator = "用户频繁修改商品";
+    }
+
+    @Data
+    public static class AbnormalPublish {
+        /**
+         * 24小时内发布同类商品数量阈值
+         */
+        private int sameCategoryCount24h = 10;
+        
+        /**
+         * 时间窗口(小时)
+         */
+        private int timeWindowHours = 24;
+
+        /**
+         * 风险类型
+         */
+        private String riskType = "异常发布";
+
+        /**
+         * 检测指标
+         */
+        private String detectIndicator = "短时间大量发布同类商品";
+    }
+}

+ 24 - 0
alien-service-config/src/main/java/shop/alien/config/properties/VideoModerationProperties.java

@@ -0,0 +1,24 @@
+package shop.alien.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 视频审核配置属性
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "video.moderation")
+public class VideoModerationProperties {
+    
+    /**
+     * 视频审核开关
+     */
+    private boolean enabled = false;
+    
+    /**
+     * 审核失败时是否阻止商品发布
+     */
+    private boolean blockOnFailure = true;
+}

+ 200 - 0
alien-service-config/src/main/java/shop/alien/config/redis/BaseRedisService.java

@@ -0,0 +1,200 @@
+package shop.alien.config.redis;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.geo.Point;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Redis基础服务类
+ */
+@Component
+@RequiredArgsConstructor
+public class BaseRedisService {
+
+    private final StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 添加List值, 向右
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setListRight(String key, String value) {
+        stringRedisTemplate.opsForList().rightPush(key, value);
+    }
+
+    /**
+     * 取出并删除列表中的所有元素(原子操作)
+     */
+    public List<String> popBatchFromList(String key) {
+        String luaScript =
+                "local elements = redis.call('LRANGE', KEYS[1], 0, -1) " +
+                        "redis.call('DEL', KEYS[1]) " +
+                        "return elements ";
+
+        RedisScript<List> script = RedisScript.of(luaScript, List.class);
+        return stringRedisTemplate.execute(script, Collections.singletonList(key));
+    }
+
+    /**
+     * 添加List, 所有
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setList(String key, List<String> value) {
+        stringRedisTemplate.opsForList().rightPushAll(key, value);
+    }
+
+    /**
+     * 从List左边取值并删除
+     *
+     * @param key 键
+     * @return 删除的值
+     */
+    public String getListLeft(String key) {
+        return stringRedisTemplate.opsForList().leftPop(key);
+    }
+
+    /**
+     * 获取List所有
+     *
+     * @param key 键
+     * @return List<String>
+     */
+    public List<String> getList(String key) {
+        return stringRedisTemplate.opsForList().range(key, 0, -1);
+    }
+
+
+    /**
+     * 添加Set
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setSetList(String key, String value) {
+        stringRedisTemplate.opsForSet().add(key, value);
+    }
+
+    /**
+     * 获取Set
+     *
+     * @param key 键
+     * @return Set<String>
+     */
+    public Set<String> getSetList(String key) {
+        return stringRedisTemplate.opsForSet().members(key);
+    }
+
+    /**
+     * 添加String值, 不设置过期时间
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setString(String key, String value) {
+        set(key, value, null);
+    }
+
+    /**
+     * 添加String值, 并设置过期时间
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     */
+    public void setString(String key, String value, Long timeOut) {
+        set(key, value, timeOut);
+    }
+
+    /**
+     * 判断是否存在指定key
+     * @param key
+     * @return
+     */
+    public boolean hasKey(String key) {
+        Boolean exists = stringRedisTemplate.hasKey(key);
+        return exists != null && exists;
+    }
+    
+    /**
+     * 添加String值, 如果key不存在则设置成功并返回true,否则返回false
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     * @return 是否设置成功
+     */
+    public Boolean setStringIfAbsent(String key, String value, Long timeOut) {
+        return stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeOut, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 设置超时时间
+     *
+     * @param key     键
+     * @param timeOut 时长(秒)
+     */
+    public void setTimeOut(String key, Long timeOut) {
+        stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 传入object对象
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     */
+    private void set(String key, Object value, Long timeOut) {
+        if (value != null) {
+            if (value instanceof String) {
+                String setValue = (String) value;
+                stringRedisTemplate.opsForValue().set(key, setValue);
+            }
+            //设置有效期
+            if (timeOut != null) {
+                stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
+            }
+        }
+    }
+
+    /**
+     * 使用key查找redis信息
+     *
+     * @param key 键
+     * @return
+     */
+    public String getString(String key) {
+        return stringRedisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 使用key删除redis信息
+     *
+     * @param key 键
+     */
+    public void delete(String key) {
+        stringRedisTemplate.delete(key);
+    }
+
+    /**
+     * 添加地理信息
+     *
+     * @param point
+     * @param content
+     * @param type
+     * @return
+     */
+    public Long inGeolocation(Point point, String content, String type) {
+        return stringRedisTemplate.opsForGeo().add(type, point, content);
+    }
+}

+ 73 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/EssentialCityCode.java

@@ -0,0 +1,73 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * <p>
+ * 区域编码
+ * </p>
+ *
+ * @author ssk
+ * @since 2025-04-23
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value="EssentialCityCode对象", description="区域编码")
+public class EssentialCityCode extends Model<EssentialCityCode> {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键id")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "地区名称")
+    @TableField("area_name")
+    private String areaName;
+
+    @ApiModelProperty(value = "地区编码")
+    @TableField("area_code")
+    private Integer areaCode;
+
+    @ApiModelProperty(value = "城市编码")
+    @TableField("city_code")
+    private Integer cityCode;
+
+    @ApiModelProperty(value = "删除标识(0 未删除 1 删除)")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField(value = "created_user_id", fill = FieldFill.INSERT)
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
+    private Integer updatedUserId;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 70 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/LifeNotice.java

@@ -0,0 +1,70 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 疑似公告
+ */
+@Data
+@JsonInclude
+@TableName("life_notice")
+public class LifeNotice {
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private String id;
+
+    @ApiModelProperty(value = "发表公告人id")
+    private String senderId;
+
+    @ApiModelProperty(value = "接收人id")
+    private String receiverId;
+
+    @ApiModelProperty(value = "业务id  关注通知-粉丝id;动态通知-动态id;")
+    @TableField("business_id")
+    private Integer businessId;
+
+    @ApiModelProperty(value = "标题")
+    private String title;
+
+    @ApiModelProperty(value = "公告内容")
+    private String context;
+
+    @ApiModelProperty(value = "公告类型  (0-系统通知和订单提醒之外的类型 1-系统通知 2-订单提醒)")
+    private Integer noticeType;
+
+    @ApiModelProperty(value = "是否已读  0:未读  1:已读")
+    private Integer isRead;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "业务类型0-其他,1-律师模块通知")
+    @TableField("business_type")
+    private Integer businessType;
+}

+ 54 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/LifeUserLearningRecord.java

@@ -0,0 +1,54 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@JsonInclude
+@TableName("life_user_learning_record")
+@ApiModel(value = "LifeUserLearningRecord对象", description = "用户学习记录")
+public class LifeUserLearningRecord {
+
+    @ApiModelProperty(value = "主键id")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "用户id")
+    @TableField("user_id")
+    private Integer userId;
+
+    @ApiModelProperty(value = "学习id")
+    @TableField("learning_id")
+    private Integer learningId;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+}
+

+ 62 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/LifeUserLearningVideo.java

@@ -0,0 +1,62 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@JsonInclude
+@TableName("life_user_learning_video")
+@ApiModel(value = "LifeUserLearningVideo对象", description = "学习视频")
+public class LifeUserLearningVideo {
+
+    @ApiModelProperty(value = "主键id")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "名称")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty(value = "简介")
+    @TableField("profile")
+    private String profile;
+
+    @ApiModelProperty(value = "奖励分数")
+    @TableField("score")
+    private Integer score;
+
+    @ApiModelProperty(value = "视频地址")
+    @TableField("video_url")
+    private String videoUrl;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+}
+

+ 66 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/SecondAiTask.java

@@ -0,0 +1,66 @@
+package shop.alien.entity.store;
+
+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 io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * second_ai_task 表实体
+ */
+@Data
+@TableName("second_ai_task")
+@ApiModel(value = "SecondAiTask", description = "AI视频审核任务表")
+public class SecondAiTask implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("数据ID")
+    @TableField("data_id")
+    private String dataId;
+
+    @ApiModelProperty("视频URL")
+    @TableField("video_url")
+    private String videoUrl;
+
+    @ApiModelProperty("任务ID")
+    @TableField("task_id")
+    private String taskId;
+
+    @ApiModelProperty("任务状态 SUBMITTED/PROCESSING/...")
+    @TableField("status")
+    private String status;
+
+    @ApiModelProperty("风险级别 none/low/medium/high")
+    @TableField("risk_level")
+    private String riskLevel;
+
+    @ApiModelProperty("审核结果(JSON)")
+    @TableField("result")
+    private String result;
+
+    @ApiModelProperty("重试次数")
+    @TableField("retry_count")
+    private Integer retryCount;
+
+    @ApiModelProperty("创建时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty("更新时间")
+    @TableField("update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}

+ 120 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/dto/LifeUserViolationDto.java

@@ -0,0 +1,120 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * <p>
+ * 用户举报
+ * </p>
+ *
+ * @author ssk
+ * @since 2025-04-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value="LifeDiscountCouponUser对象", description="优惠券用户表")
+public class LifeUserViolationDto {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @ApiModelProperty(value = "被举报用户类型")
+    private String reportedUserType;
+
+    @ApiModelProperty(value = "被举报用户ID")
+    private String reportedUserId;
+
+    @ApiModelProperty(value = "举报用户类型")
+    private String reportingUserType;
+
+    @ApiModelProperty(value = "举报用户ID")
+    private String reportingUserId;
+
+    @ApiModelProperty(value = "举报内容分类")
+    private String reportContextType;
+
+    @ApiModelProperty(value = "1:用户违规,2:色情低俗,3:违法违规,4:谩骂嘲讽、煽动对立,5:涉嫌诈骗,6:人身攻击,7:种族歧视,8:政治敏感,9:虚假、不实内容,违反公德秩序,10:危害人身安全,11:网络暴力,12:其他原因")
+    private String violationType;
+
+    @ApiModelProperty(value = "其他原因具体内容")
+    private String otherReasonContent;
+
+    @ApiModelProperty(value = "举报凭证图片")
+    private String reportEvidenceImg;
+
+    @ApiModelProperty(value = "处理状态(0:未处理,1:违规,2:未违规)")
+    private String processingStatus;
+
+    @ApiModelProperty(value = "处理时间")
+    private Date processingTime;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新时间")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "评论ID")
+    private String commentId;
+
+    @ApiModelProperty(value = "动态ID")
+    private String  dynamicsId;
+
+    @ApiModelProperty(value = "昵称")
+    private String nickname;
+
+    @ApiModelProperty(value = "联系方式")
+    private String phone;
+
+
+    @ApiModelProperty(value = "被举报人账号")
+    private String account;
+
+
+    @ApiModelProperty(value = "图片")
+    private String image;
+
+
+    @ApiModelProperty(value = "举报结果")
+    private String reportResult;
+
+
+    @ApiModelProperty(value = "图片List")
+    private List<String> imageList;
+
+
+    @ApiModelProperty(value = "举报内容")
+    private String LifeNotice;
+
+    //  举报类型状态名称
+    @ApiModelProperty(value = "举报类型状态名称")
+    private String violationTypeName;
+
+    // 举报类型名称
+    @ApiModelProperty(value = "举报类型名称")
+    private String reportContextName;
+
+    // 处理状态名称
+    @ApiModelProperty(value = "处理状态名称")
+    private String processingName;
+
+}

+ 59 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/vo/LifeFansVo.java

@@ -0,0 +1,59 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@JsonInclude
+@NoArgsConstructor
+@ApiModel(value = "LifeFansVo对象", description = "关注相关")
+public class LifeFansVo {
+
+    @ApiModelProperty(value = "用户id")
+    public String id;
+
+    @ApiModelProperty(value = "名称")
+    public String name;
+
+    @ApiModelProperty(value = "简介")
+    public String blurb;
+
+    @ApiModelProperty(value = "头像")
+    public String image;
+
+    @ApiModelProperty(value = "唯一标识")
+    public String phoneId;
+
+    @ApiModelProperty(value = "对方是否关注我 0-未关注 1-已关注")
+    public String isFollowMe;
+
+    @ApiModelProperty(value = "我是否关注对方 0-未关注 1-已关注")
+    public String isFollowThis;
+
+    @ApiModelProperty(value = "粉丝数量")
+    public String fansNum;
+
+    @ApiModelProperty(value = "关注数量")
+    public String followNum;
+
+    @ApiModelProperty(value = "好友数量")
+    public String friendNum;
+
+    @ApiModelProperty(value = "动态数量")
+    public String dynamicsNum;
+
+    @ApiModelProperty(value = "是否拉黑")
+    public String isBlocked;
+
+    @ApiModelProperty(value = "拉黑id")
+    public String blackListid;
+
+    @ApiModelProperty(value = "用户名")
+    public String username;
+
+    @ApiModelProperty(value = "用户简介")
+    public String accountBlurb;
+}

+ 19 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/vo/LifeUserLearningVideoVo.java

@@ -0,0 +1,19 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import shop.alien.entity.store.LifeUserLearningVideo;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value="LifeUserLearningVideoVo对象")
+public class LifeUserLearningVideoVo extends LifeUserLearningVideo {
+
+    @ApiModelProperty(value = "是否看过  0-没看过 1-看过")
+    private String isWatch;
+
+}

+ 41 - 0
alien-service-entity/src/main/java/shop/alien/entity/store/vo/LifeUserViolationVo.java

@@ -0,0 +1,41 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import shop.alien.entity.store.LifeUserViolation;
+
+/**
+ * @Author: fcw
+ * @CreateTime: 2025-05-08
+ * @Description: 举报
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value="LifeUserViolationVo对象", description="举报vo")
+public class LifeUserViolationVo extends LifeUserViolation {
+
+    private String reportObject;
+
+    //举报结果通知
+    private String reportResultNotification;
+
+    
+    private String phone;
+
+    private String nickName;
+
+    private String image;
+
+    //  举报类型状态名称
+    private String violationTypeName;
+
+    // 举报类型名称
+    private String reportContextName;
+
+    // 处理状态名称
+    private String processingName;
+}

Някои файлове не бяха показани, защото твърде много файлове са промени