Przeglądaj źródła

二手商品初始流程检验

wxd 1 miesiąc temu
rodzic
commit
15da339432

+ 105 - 0
alien-entity/src/main/java/shop/alien/entity/second/SecondGoods.java

@@ -0,0 +1,105 @@
+package shop.alien.entity.second;
+
+
+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.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 二手商品
+ */
+@Data
+@JsonInclude
+@TableName("second_goods")
+public class SecondGoods implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    @ApiModelProperty(value = "主键ID")
+    private Integer id;
+
+    @TableField("user_id")
+    @ApiModelProperty(value = "用户ID")
+    private Integer userId;
+
+    @TableField("title")
+    @ApiModelProperty(value = "商品标题")
+    private String title;
+
+    @TableField("describe")
+    @ApiModelProperty(value = "商品描述")
+    private String describe;
+
+    @TableField("price")
+    @ApiModelProperty(value = "商品价格(元,保留小数后两位)")
+    private BigDecimal price;
+
+    @TableField("position")
+    @ApiModelProperty(value = "商品位置")
+    private String position;
+
+    @TableField("like_count")
+    @ApiModelProperty(value = "点赞数量")
+    private Integer likeCount;
+
+    @TableField("collect_count")
+    @ApiModelProperty(value = "收藏数量")
+    private Integer collectCount;
+
+    @TableField("category_one_id")
+    @ApiModelProperty(value = "一级类别ID")
+    private Integer categoryOneId;
+
+    @TableField("category_two_id")
+    @ApiModelProperty(value = "二级类别ID")
+    private Integer categoryTwoId;
+
+    @TableField("label")
+    @ApiModelProperty(value = "标签")
+    private String label;
+
+    @TableField("topic")
+    @ApiModelProperty(value = "话题(以逗号分隔)")
+    private String topic;
+
+    @TableField("trade_id")
+    @ApiModelProperty(value = "交易ID")
+    private Integer tradeId;
+
+    @TableField("goods_status")
+    @ApiModelProperty(value = "商品状态 0:草稿 1:审核中 2:审核失败 3:已上架 4:已下架 5:已售出")
+    private Integer goodsStatus;
+
+    @TableField("failed_reason")
+    @ApiModelProperty(value = "审核失败原因")
+    private String failedReason;
+
+    @TableField("delete_flag")
+    @ApiModelProperty(value = "删除标记 0:未删除 1:已删除")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "创建时间")
+    private Date createdTime;
+
+    @TableField("created_user_id")
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createdUserId;
+
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "修改时间")
+    private Date updatedTime;
+
+    @TableField("updated_user_id")
+    @ApiModelProperty(value = "修改人ID")
+    private Integer updatedUserId;
+}

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

@@ -0,0 +1,86 @@
+package shop.alien.second.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.second.entity.SecondGoodsDTO;
+import shop.alien.second.service.SecondGoodsService;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import java.util.List;
+
+/**
+ * 二手商品控制器
+ */
+@RestController
+@RequestMapping("/secondGoods")
+@Api(tags = "二手商品管理")
+public class SecondGoodsController {
+
+    @Autowired
+    private SecondGoodsService secondGoodsService;
+
+    /**
+     * 获取所有二手商品
+     */
+    @GetMapping
+    @ApiOperation("获取所有二手商品")
+    public R<List<SecondGoods>> getAllSecondGoods() {
+        return R.data(secondGoodsService.list(), "获取成功");
+    }
+
+    /**
+     * 根据ID获取二手商品
+     */
+    @GetMapping("/{id}")
+    @ApiOperation("根据ID获取二手商品")
+    public R<SecondGoods> getSecondGoodsById(@ApiParam("商品ID") @PathVariable Integer id) {
+        return R.data(secondGoodsService.getById(id), "获取成功");
+    }
+
+    /**
+     * 添加二手商品
+     */
+    @PostMapping
+    @ApiOperation("添加二手商品")
+    public R<Void> addSecondGoods(@ApiParam("二手商品信息") @RequestBody SecondGoodsDTO secondGoods) {
+        if (!secondGoodsService.createBasicInfo(secondGoods)) {
+            return R.fail("添加二手商品失败");
+        }
+        return R.success("添加二手商品保存");
+    }
+    @PostMapping("/draft")
+    @ApiOperation("保存商品为草稿")
+    public R<Void> saveDraft(@RequestBody SecondGoods goods) {
+        if (!secondGoodsService.saveAsDraft(goods)) {
+            return R.fail("保存草稿失败");
+        }
+        return R.success("草稿已保存");
+    }
+
+    /**
+     * 更新二手商品
+     */
+    @PutMapping("/{id}")
+    @ApiOperation("更新二手商品")
+    public R<Void> updateSecondGoods(@ApiParam("商品ID") @PathVariable Integer id, @ApiParam("更新后的商品信息") @RequestBody SecondGoods secondGoods) {
+        secondGoods.setId(id);
+        secondGoodsService.updateById(secondGoods);
+        return R.success("更新成功");
+    }
+
+    /**
+     * 删除二手商品
+     */
+    @DeleteMapping("/{id}")
+    @ApiOperation("删除二手商品")
+    public R<Void> deleteSecondGoods(@ApiParam("商品ID") @PathVariable Integer id) {
+        secondGoodsService.removeById(id);
+        return R.success("删除成功");
+    }
+}

+ 112 - 0
alien-second/src/main/java/shop/alien/second/entity/SecondGoodsDTO.java

@@ -0,0 +1,112 @@
+package shop.alien.second.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.netease.yidun.sdk.core.validation.limitation.NotBlank;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+@Data
+@Api
+public class SecondGoodsDTO {
+
+    @TableId(value = "id", type = IdType.AUTO)
+    @ApiModelProperty(value = "主键ID")
+    private Integer id;
+
+    @TableField("user_id")
+    @ApiModelProperty(value = "用户ID")
+    private Integer userId;
+
+    @NotBlank(message = "商品图片不能为空")
+    @ApiModelProperty(value = "商品图片")
+    private List<String> imgUrl;
+
+    @TableField("title")
+    @NotBlank(message = "商品标题不能为空")
+    @ApiModelProperty(value = "商品标题")
+    private String title;
+
+    @TableField("describe")
+    @NotBlank(message = "商品描述不能为空")
+    @ApiModelProperty(value = "商品描述")
+    private String describe;
+
+    @TableField("price")
+    @NotBlank(message = "商品价格不能为空")
+    @ApiModelProperty(value = "商品价格(元,保留小数后两位)")
+    private BigDecimal price;
+
+    @TableField("position")
+    @NotBlank(message = "商品位置不能为空")
+    @ApiModelProperty(value = "商品位置")
+    private String position;
+
+    @TableField("like_count")
+    @ApiModelProperty(value = "点赞数量")
+    private Integer likeCount;
+
+    @TableField("collect_count")
+    @ApiModelProperty(value = "收藏数量")
+    private Integer collectCount;
+
+    @TableField("category_one_id")
+    @NotBlank(message = "一级类别ID不能为空")
+    @ApiModelProperty(value = "一级类别ID")
+    private Integer categoryOneId;
+
+    @TableField("category_two_id")
+    @NotBlank(message = "二级类别ID不能为空")
+    @ApiModelProperty(value = "二级类别ID")
+    private Integer categoryTwoId;
+
+    @TableField("label")
+    @NotBlank(message = "标签不能为空")
+    @ApiModelProperty(value = "标签")
+    private String label;
+
+    @TableField("topic")
+    @NotBlank(message = "话题不能为空")
+    @ApiModelProperty(value = "话题(以逗号分隔)")
+    private String topic;
+
+    @TableField("trade_id")
+    @ApiModelProperty(value = "交易ID")
+    private Integer tradeId;
+
+    @TableField("goods_status")
+    @ApiModelProperty(value = "商品状态 0:草稿 1:审核中 2:审核失败 3:已上架 4:已下架 5:已售出")
+    private Integer goodsStatus;
+
+    @TableField("failed_reason")
+    @ApiModelProperty(value = "审核失败原因")
+    private String failedReason;
+
+    @TableField("delete_flag")
+    @ApiModelProperty(value = "删除标记 0:未删除 1:已删除")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "创建时间")
+    private Date createdTime;
+
+    @TableField("created_user_id")
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createdUserId;
+
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "修改时间")
+    private Date updatedTime;
+
+    @TableField("updated_user_id")
+    @ApiModelProperty(value = "修改人ID")
+    private Integer updatedUserId;
+}

+ 39 - 0
alien-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);
+    }
+}

+ 27 - 0
alien-second/src/main/java/shop/alien/second/mapper/SecondGoodsMapper.java

@@ -0,0 +1,27 @@
+package shop.alien.second.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper; // 引入 Mapper 注解
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import shop.alien.entity.second.SecondGoods;
+import java.util.List;
+
+/**
+ * 二手商品映射器
+ */
+@Mapper  // 添加 @Mapper 注解,使该接口成为 Spring 管理的 Bean
+public interface SecondGoodsMapper extends BaseMapper<SecondGoods> {
+    /**
+     * 自定义分页查询
+     */
+    List<SecondGoods> getSecondGoodsByPage(IPage<SecondGoods> page);
+    
+    /**
+     * 自定义分页查询并按距离排序
+     * @param page 分页信息
+     * @param currentLatitude 当前位置纬度
+     * @param currentLongitude 当前位置经度
+     * @return 分页后的二手商品列表
+     */
+    List<SecondGoods> getSecondGoodsByPageAndDistance(IPage<SecondGoods> page, Double currentLatitude, Double currentLongitude);
+}

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

@@ -0,0 +1,27 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.second.entity.SecondGoodsDTO;
+
+/**
+ * 二手商品服务接口
+ */
+public interface SecondGoodsService extends IService<SecondGoods> {
+
+    /**
+     * 创建二手商品基本信息
+     * @param goods 二手商品实体
+     * @return 是否成功保存
+     */
+    boolean createBasicInfo(SecondGoodsDTO goods);
+
+    /**
+     * 保存为草稿
+     * @param goods 二手商品实体
+     * @return 是否成功保存
+     */
+    boolean saveAsDraft(SecondGoods goods);
+}

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

@@ -0,0 +1,156 @@
+package shop.alien.second.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.second.entity.SecondGoodsDTO;
+import shop.alien.second.mapper.SecondGoodsMapper;
+import shop.alien.second.service.SecondGoodsService;
+import shop.alien.util.common.netease.ImageCheckUtil;
+import shop.alien.util.common.netease.TextCheckUtil;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 二手商品服务实现类
+ */
+@Service
+public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, SecondGoods> implements SecondGoodsService {
+
+    /**
+     * 保存商品为草稿状态
+     * @param goods 商品实体
+     * @return 是否成功保存
+     */
+    @Override
+    public boolean saveAsDraft(SecondGoods goods) {
+        // 设置商品状态为草稿
+        goods.setGoodsStatus(0); // TODO: 使用常量或枚举代替硬编码
+        return save(goods);
+    }
+
+
+    @Override
+    public boolean createBasicInfo(SecondGoodsDTO goodsDTO) {
+        // 实现基本信息保存逻辑
+        SecondGoods goods = new SecondGoods();
+        BeanUtils.copyProperties(goods, goodsDTO);
+
+        // 保存商品基本信息
+        if (!save(goods)) {
+            return false; // 保存失败直接返回
+        }
+
+        // 获取保存后的商品ID,用于后续业务处理
+        Integer savedGoodsId = goods.getId();
+        if (savedGoodsId == null) {
+            return false; // 如果获取不到ID,视为操作失败
+        }
+
+        // 保存商品图片信息
+        if (!saveStoreImages(savedGoodsId, goodsDTO.getImgUrl())) {
+            return false;
+        }
+
+        // 执行内容审核(图片和文本)
+        if (!performContentReviews(goods, goodsDTO)) {
+            return true; // 审核不通过时已设置状态,返回成功但标记为审核失败
+        }
+
+        // 如果所有审核都通过,设置为上架状态
+        goods.setGoodsStatus(3); // 上架
+        updateById(goods);
+
+        // 发送消息
+        sendMessage(goods);
+        return true;
+    }
+
+    /**
+     * 执行内容审核(图片和文本)
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    private boolean performContentReviews(SecondGoods goods, SecondGoodsDTO goodsDTO) {
+        // 文本审核
+        Map<String, String> textCheckResult = TextCheckUtil.check(goodsDTO.getDescribe());
+        if (!"200".equals(textCheckResult.get("code"))) {
+            // 文本审核不通过
+            goods.setGoodsStatus(2); // 审核失败
+            goods.setFailedReason("文本审核不通过:" + textCheckResult.get("msg"));
+            return false;
+        }
+
+        // 图片审核(循环处理)
+        List<String> imageUrls = goodsDTO.getImgUrl();
+        if (imageUrls != null && !imageUrls.isEmpty()) {
+            StringBuilder failReasonBuilder = new StringBuilder();
+            for (String imageUrl : imageUrls) {
+                Map<String, String> imageCheckResult = ImageCheckUtil.check(imageUrl, 2);
+                if (!"200".equals(imageCheckResult.get("code"))) {
+                    // 任一图片审核不通过则整体不通过
+                    goods.setGoodsStatus(2); // 审核失败
+                    failReasonBuilder.append("图片审核不通过:").append(imageUrl).append(" 原因:").append(imageCheckResult.get("msg")).append("; ");
+                    goods.setFailedReason(failReasonBuilder.toString());
+                    return false;
+                }
+            }
+        }
+
+        // 所有审核通过
+        goods.setGoodsStatus(3); // 上架
+        goods.setFailedReason("");
+        return true;
+    }
+
+    /**
+     * 保存商品图片信息
+     * @param savedGoodsId 保存后的商品ID
+     * @param imgUrl 图片URL列表
+     * @return 保存结果
+     */
+    private boolean saveStoreImages(Integer savedGoodsId, List<String> imgUrl) {
+        if (CollectionUtil.isEmpty(imgUrl)) {
+            return true; // 如果没有图片,则返回成功
+        }
+        for(int i = 0; i < imgUrl.size(); i++){
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(savedGoodsId);
+            storeImg.setImgType(15);
+            storeImg.setImgSort(i);
+            storeImg.setImgDescription("商品图片");
+            storeImg.setDeleteFlag(0);
+            storeImg.setCreatedTime(new Date());
+            storeImg.setUpdatedTime(new Date());
+            storeImg.setCreatedUserId(1);
+            storeImg.setUpdatedUserId(1);
+            storeImg.setImgUrl(imgUrl.get(i));
+            //  调取feign接口 保存图片 插入store_img数据库
+//            storeImgMapper.insert(storeImg);
+        }
+        return true;
+    }
+
+    /**
+     * 发送消息
+     * @param goods 商品信息
+     */
+    private void sendMessage(SecondGoods goods) {
+        // 调取feign接口 发送消息 life_notice表
+//        MessageService messageService = FeignClient.create(MessageService.class);
+//        messageService.sendMessage(message);
+//        message.setType(1);
+//        message.setCreatedTime(new Date());
+//        message.setUpdatedTime(new Date());
+//        messageMapper.insert(message);
+    }
+    
+}

+ 43 - 0
alien-second/src/main/resources/mapper/SecondGoodsMapper.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.second.mapper.SecondGoodsMapper">
+    <resultMap id="BaseResultMap" type="shop.alien.entity.second.SecondGoods">
+        <!--@mbggenerated-->
+        <id column="id" property="id" jdbcType="INTEGER"/>
+        <result column="user_id" property="userId" jdbcType="INTEGER"/>
+        <result column="title" property="title" jdbcType="VARCHAR"/>
+        <result column="describe" property="describe" jdbcType="VARCHAR"/>
+        <result column="price" property="price" jdbcType="DECIMAL"/>
+        <result column="position" property="position" jdbcType="VARCHAR"/>
+        <result column="like_count" property="likeCount" jdbcType="INTEGER"/>
+        <result column="collect_count" property="collectCount" jdbcType="INTEGER"/>
+        <result column="category_one_id" property="categoryOneId" jdbcType="INTEGER"/>
+        <result column="category_two_id" property="categoryTwoId" jdbcType="INTEGER"/>
+        <result column="label" property="label" jdbcType="VARCHAR"/>
+        <result column="topic" property="topic" jdbcType="VARCHAR"/>
+        <result column="trade_id" property="tradeId" jdbcType="INTEGER"/>
+        <result column="goods_status" property="goodsStatus" jdbcType="INTEGER"/>
+        <result column="failed_reason" property="failedReason" jdbcType="VARCHAR"/>
+        <result column="delete_flag" property="deleteFlag" jdbcType="INTEGER"/>
+        <result column="created_time" property="createdTime" jdbcType="TIMESTAMP"/>
+        <result column="created_user_id" property="createdUserId" jdbcType="INTEGER"/>
+        <result column="updated_time" property="updatedTime" jdbcType="TIMESTAMP"/>
+        <result column="updated_user_id" property="updatedUserId" jdbcType="INTEGER"/>
+    </resultMap>
+
+    <!-- 自定义分页查询 -->
+    <select id="getSecondGoodsByPage" resultType="shop.alien.entity.second.SecondGoods">
+        SELECT *
+        FROM second_goods
+        WHERE delete_flag = 0
+    </select>
+
+    <!-- 自定义分页查询并按距离排序 Haversine公式 d=rarccos(cos(ϕ1)cos(ϕ2)cos(Δλ)+sin(ϕ1)sin(ϕ2)) -->
+    <select id="getSecondGoodsByPageAndDistance" resultType="shop.alien.entity.second.SecondGoods">
+        SELECT *,
+        (6371 * acos(cos(radians(#{currentLatitude})) * cos(radians(SUBSTRING_INDEX(position, ',', 1))) * cos(radians(SUBSTRING_INDEX(position, ',', -1)) - radians(#{currentLongitude})) + sin(radians(#{currentLatitude})) * sin(radians(SUBSTRING_INDEX(position, ',', 1))))) AS distance
+        FROM second_goods
+        WHERE delete_flag = 0
+        ORDER BY distance ASC
+    </select>
+</mapper>