Browse Source

发布二手商品

chao 4 years ago
parent
commit
686b39d2e9

+ 38 - 5
src/main/java/com/caimei365/commodity/controller/SecondHandApi.java

@@ -1,6 +1,8 @@
 package com.caimei365.commodity.controller;
 
+import com.caimei365.commodity.idempotent.Idempotent;
 import com.caimei365.commodity.model.ResponseJson;
+import com.caimei365.commodity.model.dto.SecondDto;
 import com.caimei365.commodity.model.vo.PaginationVo;
 import com.caimei365.commodity.model.vo.SecondDetailVo;
 import com.caimei365.commodity.model.vo.SecondListVo;
@@ -10,11 +12,8 @@ import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.*;
 
 /**
  * Description
@@ -60,6 +59,40 @@ public class SecondHandApi {
         return secondHandService.getSecondHandDetail(productId);
     }
 
+    /**
+     * 发布二手商品
+     *
+     * @param secondDto 对象内容描述{
+     *                  "secondHandType" : 二手商品分类,1二手仪器,2临期产品,3其他
+     *                  ,"instrumentType" : 二手仪器分类的类型,1轻光电、2重光电、3耗材配件(仅适用于二手仪器分类多个用英文逗号分分隔)
+     *                  ,"brandId" : 品牌Id
+     *                  ,"name" : 商品名称
+     *                  ,"fixedYears" : 出厂日期格式:2020年6月
+     *                  ,"maturityYears" : 产品到期日格式:2020年6月(仅适用于临期产品)
+     *                  ,"companyName" : 公司名称
+     *                  ,"price" : 交易价格
+     *                  ,"detailTalkFlag" : 是否启用详聊,1不开启,2开启(开启详聊不展示交易价)
+     *                  ,"normalPrice" : 市场价
+     *                  ,"originalPrice" : 采购价/原价(该二手原始购买价格)
+     *                  ,"stock" : 数量(等同库存)
+     *                  ,"productQuality" : 商品成色
+     *                  ,"contactName" : 联系人名字
+     *                  ,"contactMobile" : 联系方式
+     *                  ,"secondProductType" : 设备类型:1医美、2非医美
+     *                  ,"townId" : 县区ID
+     *                  ,"address" : 详细地址
+     *                  ,"image" : 图片信息
+     *                  ,"productDetails" : 商品详细信息
+     *                  ,"source" : 信息来源 1网站  2CRM  3后台
+     *                  }
+     * @param headers   HttpHeaders
+     */
+    @ApiOperation("发布二手商品(旧:/product/releaseSecondHandProduct)")
+    @Idempotent(prefix = "idempotent_secondhand", keys = {"#secondDto"}, expire = 5)
+    @PostMapping("/release")
+    public ResponseJson releaseSecondHand(SecondDto secondDto, @RequestHeader HttpHeaders headers){
+        return secondHandService.releaseSecondHand(secondDto, headers);
+    }
 
 
 }

+ 27 - 0
src/main/java/com/caimei365/commodity/idempotent/Idempotent.java

@@ -0,0 +1,27 @@
+package com.caimei365.commodity.idempotent;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义幂等注解
+ *
+ * @author : Charles
+ * @date : 2021/2/26
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Idempotent {
+    /**
+     * 前缀属性,作为redis缓存Key的一部分。
+     */
+    String prefix() default "idempotent_";
+    /**
+     * 需要的参数名数组
+     */
+    String[] keys();
+    /**
+     * 幂等过期时间(秒),即:在此时间段内,对API进行幂等处理。
+     */
+    int expire() default 3;
+}

+ 104 - 0
src/main/java/com/caimei365/commodity/idempotent/IdempotentAspect.java

@@ -0,0 +1,104 @@
+package com.caimei365.commodity.idempotent;
+
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.stereotype.Component;
+import redis.clients.jedis.commands.JedisCommands;
+import redis.clients.jedis.params.SetParams;
+
+import javax.annotation.Resource;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+
+/**
+ * 幂等切面
+ *
+ * @author : Charles
+ * @date : 2021/2/26
+ */
+@Slf4j
+@Aspect
+@Component
+@ConditionalOnClass(RedisTemplate.class)
+public class IdempotentAspect {
+
+    private static final String LOCK_SUCCESS = "OK";
+
+    @Resource
+    private RedisTemplate<String,String> redisTemplate;
+
+    /**
+     * 切入点,根据自定义Idempotent实际路径进行调整
+     */
+    @Pointcut("@annotation(com.caimei365.user.idempotent.Idempotent)")
+    public void executeIdempotent() {
+    }
+
+    @Around("executeIdempotent()")
+    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+        // 获取参数对象列表
+        Object[] args = joinPoint.getArgs();
+      	//获取方法
+        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
+        // 得到方法名
+        String methodName = method.getName();
+        // 获取参数数组
+        //Parameter[] parameters = method.getParameters();
+        String[] parameters = new LocalVariableTableParameterNameDiscoverer().getParameterNames(method);
+
+      	//获取幂等注解
+        Idempotent idempotent = method.getAnnotation(Idempotent.class);
+
+        // 初始化springEL表达式解析器实例
+        ExpressionParser parser = new SpelExpressionParser();
+        // 初始化解析内容上下文
+        EvaluationContext context = new StandardEvaluationContext();
+        // 把参数名和参数值放入解析内容上下文里
+        for (int i = 0; i < parameters.length; i++) {
+            if (args[i] != null) {
+                // 添加解析对象目标
+                context.setVariable(parameters[i], args[i].hashCode());
+            }
+        }
+        // 解析定义key对应的值,拼接成key
+        StringBuilder idempotentKey = new StringBuilder(idempotent.prefix() + ":" + methodName);
+        for (String s : idempotent.keys()) {
+            Arrays.asList(args).contains(s);
+            // 解析对象
+            Expression expression = parser.parseExpression(s);
+            idempotentKey.append(":").append(expression.getValue(context));
+        }
+        // 通过 setnx 确保只有一个接口能够正常访问
+        String result = redisTemplate.execute(
+            (RedisCallback<String>) connection -> (
+                (JedisCommands) connection.getNativeConnection()
+            ).set(
+                idempotentKey.toString(),
+                idempotentKey.toString(),
+                new SetParams().nx().ex(idempotent.expire())
+            )
+        );
+
+        if (LOCK_SUCCESS.equals(result)) {
+            return joinPoint.proceed();
+        } else {
+            log.error("API幂等处理, key=" + idempotentKey);
+            throw new IdempotentException("手速太快了,稍后重试!");
+        }
+    }
+}
+

+ 19 - 0
src/main/java/com/caimei365/commodity/idempotent/IdempotentException.java

@@ -0,0 +1,19 @@
+package com.caimei365.commodity.idempotent;
+
+/**
+ * 处理幂等相关异常
+ *
+ * @author : Charles
+ * @date : 2021/2/26
+ */
+public class IdempotentException extends RuntimeException {
+
+    public IdempotentException(String message) {
+        super(message);
+    }
+
+    @Override
+    public String getMessage() {
+        return super.getMessage();
+    }
+}

+ 26 - 0
src/main/java/com/caimei365/commodity/idempotent/IdempotentExceptionHandler.java

@@ -0,0 +1,26 @@
+package com.caimei365.commodity.idempotent;
+
+
+import com.caimei365.commodity.model.ResponseJson;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * ApI幂等异常处理
+ *
+ * @author : Charles
+ * @date : 2021/3/29
+ */
+@Slf4j
+@ControllerAdvice
+public class IdempotentExceptionHandler {
+    @ExceptionHandler(IdempotentException.class)
+    @ResponseBody
+    public ResponseJson convertExceptionMsg(Exception e) {
+        //自定义逻辑,可返回其他值
+        log.error("ApI幂等错误拦截,错误信息:", e);
+        return ResponseJson.error("幂等异常处理:" + e.getMessage());
+    }
+}

+ 22 - 0
src/main/java/com/caimei365/commodity/mapper/SecondHandMapper.java

@@ -1,5 +1,9 @@
 package com.caimei365.commodity.mapper;
 
+import com.caimei365.commodity.model.po.ProductImagePo;
+import com.caimei365.commodity.model.po.ProductPo;
+import com.caimei365.commodity.model.po.ProductSecondPo;
+import com.caimei365.commodity.model.vo.AddressVo;
 import com.caimei365.commodity.model.vo.SecondDetailVo;
 import com.caimei365.commodity.model.vo.SecondListVo;
 import org.apache.ibatis.annotations.Mapper;
@@ -32,4 +36,22 @@ public interface SecondHandMapper {
      */
     List<String> getImageByProductId(Integer productId);
 
+    /**
+     * 保存商品二手附加详细信息
+     * @param secondPo ProductSecondPo
+     */
+    void saveSencondHandProduct(ProductSecondPo secondPo);
+    /**
+     * 二手商品通过地址ID获取详细省市区
+     */
+    AddressVo getAddressInfo(Integer townId);
+    /**
+     * 保存商品图片信息
+     */
+    void insertProductImage(ProductImagePo imagePo);
+
+    /**
+     * 保存商品表
+     */
+    void insertProduct(ProductPo product);
 }

+ 130 - 0
src/main/java/com/caimei365/commodity/model/dto/SecondDto.java

@@ -0,0 +1,130 @@
+package com.caimei365.commodity.model.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/4/16
+ */
+@ApiModel("二手商品发布")
+@Data
+public class SecondDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 二手商品分类,1二手仪1器,2临期产品,3其他
+     */
+    @ApiModelProperty("二手商品分类,1二手仪1器,2临期产品,3其他")
+    private String secondHandType;
+    /**
+     * 二手仪器分类的类型,1轻光电、2重光电、3耗材配件(仅适用于二手仪器分类多个用英文逗号分分隔)
+     */
+    @ApiModelProperty("二手仪器分类的类型,1轻光电、2重光电、3耗材配件")
+    private String instrumentType;
+    /**
+     * 品牌Id
+     */
+    @ApiModelProperty("品牌Id(旧:brandID)")
+    private Integer brandId;
+    /**
+     * 商品名称
+     */
+    @ApiModelProperty("商品名称")
+    private String name;
+    /**
+     * 商品价格
+     */
+    @ApiModelProperty("交易价格(旧:price1)")
+    private Double price;
+    /**
+     * 市场价
+     */
+    @ApiModelProperty("市场价")
+    private Double normalPrice;
+    /**
+     * 采购价/原价(该二手原始购买价格)
+     */
+    @ApiModelProperty("采购价/原价")
+    private Double originalPrice;
+    /**
+     * 是否启用详聊,1不开启,2开启(开启详聊不展示交易价)
+     */
+    @ApiModelProperty("是否启用详聊,1不开启,2开启")
+    private String detailTalkFlag;
+    /**
+     * 数量(等同库存)
+     */
+    @ApiModelProperty("数量(等同库存)")
+    private Integer stock;
+    /**
+     * 出厂日期格式:2020年6月
+     */
+    @ApiModelProperty("出厂日期")
+    private String fixedYears;
+    /**
+     * 产品到期日格式:2020年6月(仅适用于临期产品)
+     */
+    @ApiModelProperty("商品名称")
+    private String maturityYears;
+    /**
+     * 公司名称
+     */
+    @ApiModelProperty("公司名称")
+    private String companyName;
+    /**
+     * 商品成色
+     */
+    @ApiModelProperty("商品成色")
+    private String productQuality;
+    /**
+     * 联系人名字
+     */
+    @ApiModelProperty("联系人名字")
+    private String contactName;
+    /**
+     * 联系方式
+     */
+    @ApiModelProperty("联系方式")
+    private String contactMobile;
+    /**
+     * 对接人名称
+     */
+    @ApiModelProperty("对接人名称")
+    private String dockingPeopleName;
+    /**
+     * 设备类型:1医美、2非医美
+     */
+    @ApiModelProperty("设备类型:1医美、2非医美")
+    private String secondProductType;
+    /**
+     * 商品详情信息(补充信息)
+     */
+    @ApiModelProperty("商品详情信息")
+    private String productDetails;
+    /**
+     * 商品图片信息
+     */
+    @ApiModelProperty("商品图片信息(旧:image1)")
+    private String image;
+    /**
+     * 区Id
+     */
+    @ApiModelProperty("区Id")
+    private Integer townId;
+    /**
+     * 详细联系地址
+     */
+    @ApiModelProperty("详细联系地址")
+    private String address;
+    /**
+     * 信息来源 1网站  2CRM  3后台
+     */
+    @ApiModelProperty("信息来源:1网站,2CRM,3后台")
+    private String source;
+
+}

+ 34 - 0
src/main/java/com/caimei365/commodity/model/po/ProductImagePo.java

@@ -0,0 +1,34 @@
+package com.caimei365.commodity.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/4/16
+ */
+@Data
+public class ProductImagePo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /** 商品productID */
+    private Integer productId;
+    /**
+     * 所属供应商Id
+     */
+    private Integer shopId;
+    /**
+     * 添加时间
+     */
+    private String addTime;
+    /**
+     * 图
+     */
+    private String image;
+    /**
+     * 是否主图:1是,空或0不是
+     */
+    private Integer mainFlag;
+}

+ 153 - 0
src/main/java/com/caimei365/commodity/model/po/ProductPo.java

@@ -0,0 +1,153 @@
+package com.caimei365.commodity.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.lang.Double;
+import java.util.Date;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/4/16
+ */
+@Data
+public class ProductPo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /** 商品productID */
+    private Integer productId;
+    /**
+     * 品牌Id
+     */
+    private Integer brandId;
+    /**
+     * 商品名称
+     */
+    private String name;
+    /**
+     * 商品价格
+     */
+    private Double price;
+    /**
+     * 库存
+     */
+    private Integer stock;
+    /**
+     * 内部商品名称
+     */
+    private String aliasName;
+    /**
+     * 市场价
+     */
+    private Double normalPrice;
+    /**
+     * 主图
+     */
+    private String mainImage;
+    /**
+     * 商品的类别:1正常商品(默认),2二手商品
+     */
+    private Integer productCategory;
+    /**
+     * 常用商品001,精品推荐010,热门推荐100,三者同时存在111
+     */
+    private Integer preferredFlag;
+    /**
+     * 所属供应商Id
+     */
+    private Integer shopId;
+    /**
+     * 销量
+     */
+    private Integer sellNumber;
+    /**
+     * 成本价
+     */
+    private Double costPrice;
+    /**
+     * 成本价选中标志:1固定成本 2比例成
+     */
+    private Integer costCheckFlag;
+    /**
+     * 是否有sku:1有, 0没有
+     */
+    private Integer hasSkuFlag;
+    /**
+     * 商品状态, 0逻辑删除 1待审核 2已上架 3已下架 8审核未通过 9已隐身 10已冻结
+     */
+    private Integer validFlag;
+    /**
+     * 启用阶梯价格标识 0否 1是
+     */
+    private Integer ladderPriceFlag;
+    /**
+     * 排序值
+     */
+    private Integer sortIndex;
+    /**
+     * 供应商主推商品标志 0否 1是
+     */
+    private Integer featuredFlag;
+    /**
+     * 运费:0买家承担, 1卖家承担
+     */
+    private Integer byFlag;
+    /**
+     * 购买数量: 1逐步增长,2以起订量增长(起订量的倍数增长)
+     */
+    private Integer step;
+    /**
+     * 是否使用活动角标:1是,空或0不是[与actType搭配使用,仅用于标识非真正活动]
+     */
+    private Integer actFlag;
+    /**
+     * 商品是否处于活动状态:1是,空或0不是[活动商品和actFlag含义不同]
+     */
+    private Integer actStatus;
+    /**
+     * 是否包邮 0包邮 1到付
+     */
+    private Integer freePostFlag;
+    /**
+     * 商品类型:0其它类型(默认),1妆字号,2械字号
+     */
+    private Integer productType;
+    /**
+     * 械字号类型   (基于械字号基础),1:一类,2:二类,3:三类
+     */
+    private Integer machineType;
+    /**
+     * 是否含税   0不含税,1含税,2未知
+     */
+    private Integer includedTax;
+    /**
+     * 相关推荐类型 0自动选择; 1手动推荐
+     */
+    private Integer recommendType;
+    /**
+     * 发票类型(基于是否含税基础)   1增值税票,2普通票, 3不能开票
+     */
+    private Integer invoiceType;
+    /**
+     * 商品可见度:(3:所有人可见,2:普通机构可见,1:会员机构可见)
+     */
+    private Integer visibility;
+    /**
+     * 添加时间
+     */
+    private String addTime;
+    /**
+     * 更新时间
+     */
+    private String updateTime;
+    /**
+     * 上架时间
+     */
+    private Date onlineTime;
+    /**
+     * 下架时间
+     */
+    private Date offlineTime;
+
+}

+ 167 - 0
src/main/java/com/caimei365/commodity/model/po/ProductSecondPo.java

@@ -0,0 +1,167 @@
+package com.caimei365.commodity.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/4/16
+ */
+@Data
+public class ProductSecondPo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /** 主键id */
+    private Integer id;
+    /** 商品productID */
+    private Integer productId;
+    /**
+     * 二手商品分类,1二手仪1器,2临期产品,3其他
+     */
+    private String secondHandType;
+    /**
+     * 二手仪器分类的类型,1轻光电、2重光电、3耗材配件(仅适用于二手仪器分类多个用英文逗号分分隔)
+     */
+    private String instrumentType;
+    /**
+     * 品牌Id
+     */
+    private Integer brandId;
+    /**
+     * 商品名称
+     */
+    private String name;
+    /**
+     * 商品价格
+     */
+    private Double price;
+    /**
+     * 市场价
+     */
+    private Double normalPrice;
+    /**
+     * 采购价/原价(该二手原始购买价格)
+     */
+    private Double originalPrice;
+    /**
+     * 是否启用详聊,1不开启,2开启(开启详聊不展示交易价)
+     */
+    private String detailTalkFlag;
+    /**
+     * 数量(等同库存)
+     */
+    private Integer stock;
+    /**
+     * 出厂日期格式:2020年6月
+     */
+    private String fixedYears;
+    /**
+     * 产品到期日格式:2020年6月(仅适用于临期产品)
+     */
+    private String maturityYears;
+    /**
+     * 公司名称
+     */
+    private String companyName;
+    /**
+     * 商品成色
+     */
+    private String productQuality;
+    /**
+     * 联系人名字
+     */
+    private String contactName;
+    /**
+     * 联系方式
+     */
+    private String contactMobile;
+    /**
+     * 对接人名称
+     */
+    private String dockingPeopleName;
+    /**
+     * 设备类型:1医美、2非医美
+     */
+    private String secondProductType;
+    /**
+     * 商品详情信息(补充信息)
+     */
+    private String productDetails;
+    /**
+     * 商品图片信息
+     */
+    private String image;
+    /**
+     * 区Id
+     */
+    private Integer townId;
+    /**
+     * 详细联系地址
+     */
+    private String address;
+    /**
+     * 信息来源 1网站  2CRM  3后台
+     */
+    private String source;
+    /**
+     * 对接人联系方式
+     */
+    private String dockingPeopleMobile;
+    /**
+     * 品牌名称
+     */
+    private String brandName;
+    /**
+     * 省市区(地址前部分)
+     */
+    private String provinceCityDistrict;
+    /**
+     * 是否已售 0和空未出售,1已出售
+     */
+    private Integer sold;
+    /**
+     * 浏览量
+     */
+    private Integer viewingNum;
+
+    /**
+     * 付款状态1:待支付、2已付款
+     */
+    private Integer payStatus;
+    /**
+     * 支付金额
+     */
+    private Double payAmount;
+    /**
+     * 线上支付回调返回数据存档
+     */
+    private String payFormData;
+    /**
+     * 付款方式 0后台付款、1:支付宝、2微信 、12PC-B2B网银、13PC-微信支付、14PC-支付宝、15小程序-微信支付
+     */
+    private String payType;
+    /**
+     * 线上付款时间
+     */
+    private Date payDate;
+    /**
+     * 提交时间
+     */
+    private Date submitDate;
+    /**
+     * 审核时间'
+     */
+    private Date reviewedDate;
+    /**
+     * 上架时间(审核时上架变化、自动下架后再上架变化、手动下架后上架如果不在有效期内才变化)
+     */
+    private Date onLineDate;
+    /**
+     * 后台二手商品发布人员名称
+     */
+    private String publisher;
+
+}

+ 32 - 0
src/main/java/com/caimei365/commodity/model/vo/AddressVo.java

@@ -0,0 +1,32 @@
+package com.caimei365.commodity.model.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/4/16
+ */
+@Data
+public class AddressVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 省份名称
+     */
+    private String province;
+    /**
+     * 城市名称
+     */
+    private String city;
+    /**
+     * 村镇名
+     */
+    private String town;
+    /**
+     * 区ID
+     */
+    private Integer townId;
+}

+ 32 - 0
src/main/java/com/caimei365/commodity/service/SecondHandService.java

@@ -1,9 +1,11 @@
 package com.caimei365.commodity.service;
 
 import com.caimei365.commodity.model.ResponseJson;
+import com.caimei365.commodity.model.dto.SecondDto;
 import com.caimei365.commodity.model.vo.PaginationVo;
 import com.caimei365.commodity.model.vo.SecondDetailVo;
 import com.caimei365.commodity.model.vo.SecondListVo;
+import org.springframework.http.HttpHeaders;
 
 /**
  * Description
@@ -31,4 +33,34 @@ public interface SecondHandService {
      * @return SecondDetailVo
      */
     ResponseJson<SecondDetailVo> getSecondHandDetail(Integer productId);
+
+    /**
+     * 发布二手商品
+     *
+     * @param secondDto 对象内容描述{
+     *                  "secondHandType" : 二手商品分类,1二手仪器,2临期产品,3其他
+     *                  ,"instrumentType" : 二手仪器分类的类型,1轻光电、2重光电、3耗材配件(仅适用于二手仪器分类多个用英文逗号分分隔)
+     *                  ,"brandId" : 品牌Id
+     *                  ,"name" : 商品名称
+     *                  ,"fixedYears" : 出厂日期格式:2020年6月
+     *                  ,"maturityYears" : 产品到期日格式:2020年6月(仅适用于临期产品)
+     *                  ,"companyName" : 公司名称
+     *                  ,"price" : 交易价格
+     *                  ,"detailTalkFlag" : 是否启用详聊,1不开启,2开启(开启详聊不展示交易价)
+     *                  ,"normalPrice" : 市场价
+     *                  ,"originalPrice" : 采购价/原价(该二手原始购买价格)
+     *                  ,"stock" : 数量(等同库存)
+     *                  ,"productQuality" : 商品成色
+     *                  ,"contactName" : 联系人名字
+     *                  ,"contactMobile" : 联系方式
+     *                  ,"secondProductType" : 设备类型:1医美、2非医美
+     *                  ,"townId" : 县区ID
+     *                  ,"address" : 详细地址
+     *                  ,"image" : 图片信息
+     *                  ,"productDetails" : 商品详细信息
+     *                  ,"source" : 信息来源 1网站  2CRM  3后台
+     *                  }
+     * @param headers   HttpHeaders
+     */
+    ResponseJson releaseSecondHand(SecondDto secondDto, HttpHeaders headers);
 }

+ 244 - 2
src/main/java/com/caimei365/commodity/service/impl/SecondHandServiceImpl.java

@@ -1,16 +1,23 @@
 package com.caimei365.commodity.service.impl;
 
+import com.caimei365.commodity.components.RedisService;
 import com.caimei365.commodity.mapper.SecondHandMapper;
 import com.caimei365.commodity.model.ResponseJson;
+import com.caimei365.commodity.model.dto.SecondDto;
+import com.caimei365.commodity.model.po.ProductImagePo;
+import com.caimei365.commodity.model.po.ProductPo;
+import com.caimei365.commodity.model.po.ProductSecondPo;
+import com.caimei365.commodity.model.vo.AddressVo;
 import com.caimei365.commodity.model.vo.PaginationVo;
 import com.caimei365.commodity.model.vo.SecondDetailVo;
 import com.caimei365.commodity.model.vo.SecondListVo;
 import com.caimei365.commodity.service.SecondHandService;
 import com.caimei365.commodity.utils.ImageUtils;
 import com.github.pagehelper.PageHelper;
-import com.github.pagehelper.util.StringUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
 
@@ -19,7 +26,6 @@ import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
-import java.util.regex.Matcher;
 
 /**
  * Description
@@ -34,6 +40,9 @@ public class SecondHandServiceImpl implements SecondHandService {
     private String domain;
     @Resource
     private SecondHandMapper secondHandMapper;
+    @Resource
+    private RedisService redisService;
+
 
     /**
      * 二手商品列表
@@ -128,6 +137,239 @@ public class SecondHandServiceImpl implements SecondHandService {
         return ResponseJson.success(second);
     }
 
+    /**
+     * 发布二手商品
+     *
+     * @param secondDto 对象内容描述{
+     *                  "secondHandType" : 二手商品分类,1二手仪器,2临期产品,3其他
+     *                  ,"instrumentType" : 二手仪器分类的类型,1轻光电、2重光电、3耗材配件(仅适用于二手仪器分类多个用英文逗号分分隔)
+     *                  ,"brandId" : 品牌Id
+     *                  ,"name" : 商品名称
+     *                  ,"fixedYears" : 出厂日期格式:2020年6月
+     *                  ,"maturityYears" : 产品到期日格式:2020年6月(仅适用于临期产品)
+     *                  ,"companyName" : 公司名称
+     *                  ,"price" : 交易价格
+     *                  ,"detailTalkFlag" : 是否启用详聊,1不开启,2开启(开启详聊不展示交易价)
+     *                  ,"normalPrice" : 市场价
+     *                  ,"originalPrice" : 采购价/原价(该二手原始购买价格)
+     *                  ,"stock" : 数量(等同库存)
+     *                  ,"productQuality" : 商品成色
+     *                  ,"contactName" : 联系人名字
+     *                  ,"contactMobile" : 联系方式
+     *                  ,"secondProductType" : 设备类型:1医美、2非医美
+     *                  ,"townId" : 县区ID
+     *                  ,"address" : 详细地址
+     *                  ,"image" : 图片信息
+     *                  ,"productDetails" : 商品详细信息
+     *                  ,"source" : 信息来源 1网站  2CRM  3后台
+     *                  }
+     * @param headers   HttpHeaders
+     */
+    @Override
+    public ResponseJson releaseSecondHand(SecondDto secondDto, HttpHeaders headers) {
+        // 打印IP
+        String ip = headers.getFirst("X-CLIENT-IP");
+        log.info("发布二手商品 X-CLIENT-IP : " + ip);
+        String dateStr = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
+        if (StringUtils.isNotEmpty(ip)) {
+            Integer accessNumCount = 0;
+            boolean exists = redisService.exists(dateStr + ":releaseSecondHand-" + ip);
+            if (exists) {
+                accessNumCount = (Integer) redisService.get(dateStr + ":releaseSecondHand-" + ip);
+                if (accessNumCount >= 20) {
+                    return ResponseJson.error("操作太频繁,今日已不能发布");
+                }
+                accessNumCount += 1;
+            } else {
+                accessNumCount = 1;
+            }
+            //以时间串+接口名称+ip和当前时间为key,设置有效期为24小时,限制每天操作次数20次
+            redisService.set(dateStr + ":releaseSecondHand-" + ip, accessNumCount, 86400L);
+        }
+        // 验证传入参数
+        String secondHandType = secondDto.getSecondHandType();
+        String instrumentType = secondDto.getInstrumentType();
+        Integer brandId = secondDto.getBrandId();
+        String name = secondDto.getName();
+        Double price = secondDto.getPrice();
+        Double normalPrice = secondDto.getNormalPrice();
+        Double originalPrice = secondDto.getOriginalPrice();
+        Integer stock = secondDto.getStock();
+        String productQuality = secondDto.getProductQuality();
+        String contactName = secondDto.getContactName();
+        String contactMobile = secondDto.getContactMobile();
+        String source = secondDto.getSource();
+        Integer townId = secondDto.getTownId();
+        String address = secondDto.getAddress();
+        // 此图片为逗号隔开的多张数据
+        String image = secondDto.getImage();
+        String maturityYears = secondDto.getMaturityYears();
+
+        if (StringUtils.isEmpty(secondHandType)) {
+            return ResponseJson.error("参数异常:请选择分类");
+        } else if (StringUtils.equals("1", secondHandType)) {
+            if (StringUtils.isEmpty(instrumentType)) {
+                return ResponseJson.error("参数异常:请完善仪器分类");
+            }
+        } else if (StringUtils.equals("2", secondHandType)) {
+            // 选择临期产品的时候才需要设置
+            if (null == normalPrice || normalPrice <= 0) {
+                return ResponseJson.error("参数异常:请输入市场价");
+            }
+            if (null == originalPrice) {
+                return ResponseJson.error("参数异常:请输入采购价/原价");
+            }
+            if (null == stock) {
+                return ResponseJson.error("参数异常:请输入数量");
+            }
+            if (StringUtils.isEmpty(maturityYears)) {
+                return ResponseJson.error("参数异常:请输入产品到期日");
+            }
+        }
+        if (null == brandId) {
+            return ResponseJson.error("参数异常:请选择商品品牌");
+        }
+        if (StringUtils.isEmpty(name)) {
+            return ResponseJson.error("参数异常:请输入商品名称");
+        }
+        if (null == price || price <= 0) {
+            return ResponseJson.error("参数异常:请输入交易价");
+        }
+        if (StringUtils.isEmpty(contactName)) {
+            return ResponseJson.error("参数异常:请输入联系人姓名");
+        }
+        if (StringUtils.isEmpty(contactMobile)) {
+            return ResponseJson.error("参数异常:请输入联系方式");
+        }
+        if (null == townId) {
+            return ResponseJson.error("参数异常:请完善联系地址");
+        }
+        if (StringUtils.isEmpty(address)) {
+            return ResponseJson.error("参数异常:请填写详细地址");
+        }
+        if (StringUtils.isEmpty(image)) {
+            return ResponseJson.error("参数异常:请上传图片");
+        }
+        if (StringUtils.isEmpty(productQuality)) {
+            return ResponseJson.error("参数异常:请输入商品成色");
+        }
+        if (StringUtils.isEmpty(source)) {
+            return ResponseJson.error("参数异常:请输入发布来源");
+        }
+        // 保存二手商品
+        return saveSecondHandProduct(secondDto);
+    }
+
+    private ResponseJson saveSecondHandProduct(SecondDto secondDto) {
+        // 设置日期时间格式
+        Date date = new Date();
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String current = dateFormat.format(date);
+        // 图片
+        String[] images = secondDto.getImage().split(",");
+        // 组装数据
+        ProductPo product = new ProductPo();
+        product.setBrandId(secondDto.getBrandId());
+        product.setName(secondDto.getName());
+        product.setPrice(secondDto.getPrice());
+        product.setStock(secondDto.getStock());
+        product.setAliasName(secondDto.getName());
+        product.setNormalPrice(secondDto.getNormalPrice());
+        product.setMainImage(images[0]);
+        // 二手商品类型
+        product.setProductCategory(2);
+        product.setPreferredFlag(0);
+        // 默认发布到二手供应商
+        product.setShopId(1252);
+        product.setSellNumber(secondDto.getStock());
+        product.setCostPrice(0d);
+        product.setCostCheckFlag(1);
+        product.setHasSkuFlag(1);
+        // 商品状态默认待审核
+        product.setValidFlag(1);
+        product.setLadderPriceFlag(0);
+        product.setSortIndex(1);
+        product.setFeaturedFlag(0);
+        product.setByFlag(0);
+        product.setStep(1);
+        product.setActFlag(0);
+        product.setActStatus(0);
+        product.setFreePostFlag(1);
+        product.setProductType(0);
+        product.setMachineType(0);
+        product.setIncludedTax(0);
+        product.setRecommendType(0);
+        product.setInvoiceType(0);
+        product.setVisibility(3);
+        product.setAddTime(current);
+        product.setUpdateTime(current);
+        product.setOnlineTime(date);
+        product.setOfflineTime(date);
+        /* 保存商品表 */
+        secondHandMapper.insertProduct22222222222(product);
+        /* 保存商品图片信息*/
+        for (int i = 0; i < images.length; i++) {
+            ProductImagePo imagePo = new ProductImagePo();
+            imagePo.setProductId(product.getProductId());
+            imagePo.setShopId(1252);
+            imagePo.setAddTime(current);
+            imagePo.setImage(images[i]);
+            if (i == 0) {
+                imagePo.setMainFlag(1);
+            } else {
+                imagePo.setMainFlag(0);
+            }
+            secondHandMapper.insertProductImage(imagePo);
+        }
+        ProductSecondPo secondPo = new ProductSecondPo();
+        // 保存附赠详细信息关系
+        secondPo.setProductId(product.getProductId());
+        // 通过地址查询省市区
+        String provinceCityDistrict = "";
+        Integer townId = secondDto.getTownId();
+        secondPo.setTownId(townId);
+        if (null != townId) {
+            AddressVo address = secondHandMapper.getAddressInfo(townId);
+            if (null != address) {
+                provinceCityDistrict = address.getProvince() + "/" + address.getCity() + "/" + address.getTown();
+                secondPo.setProvinceCityDistrict(provinceCityDistrict);
+            }
+        }
+        // 默认设置未出售
+        secondPo.setSold(0);
+        // 付款状态1:待支付、2已付款
+        secondPo.setPayStatus(1);
+        secondPo.setPayAmount(0d);
+        secondPo.setPayType("");
+        secondPo.setSubmitDate(date);
+        secondPo.setViewingNum(0);
+        secondPo.setDockingPeopleName(secondDto.getDockingPeopleName());
+        if (StringUtils.isNotBlank(secondDto.getDockingPeopleName())) {
+            secondPo.setPublisher(secondDto.getDockingPeopleName());
+        } else {
+            secondPo.setPublisher(secondDto.getContactName());
+        }
+        secondPo.setSecondHandType(secondDto.getSecondHandType());
+        secondPo.setInstrumentType(secondDto.getInstrumentType());
+        secondPo.setFixedYears(secondDto.getFixedYears());
+        secondPo.setMaturityYears(secondDto.getMaturityYears());
+        secondPo.setCompanyName(secondDto.getCompanyName());
+        secondPo.setDetailTalkFlag(secondDto.getDetailTalkFlag());
+        secondPo.setOriginalPrice(secondDto.getOriginalPrice());
+        secondPo.setContactName(secondDto.getContactName());
+        secondPo.setContactMobile(secondDto.getContactMobile());
+        secondPo.setDockingPeopleMobile(secondDto.getDockingPeopleName());
+        secondPo.setSecondProductType(secondDto.getSecondProductType());
+        secondPo.setAddress(secondDto.getAddress());
+        secondPo.setProductQuality(secondDto.getProductQuality());
+        secondPo.setProductDetails(secondDto.getProductDetails());
+        secondPo.setSource(secondDto.getSource());
+        /* 保存商品二手附加详细信息 */
+        secondHandMapper.saveSencondHandProduct(secondPo);
+        return ResponseJson.success(secondPo.getId());
+    }
+
+
     /**
      * 金额格式化,千分位
      *

+ 83 - 0
src/main/resources/mapper/SecondHandMapper.xml

@@ -1,6 +1,80 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.caimei365.commodity.mapper.SecondHandMapper">
+    <insert id="saveSencondHandProduct" parameterType="com.caimei365.commodity.model.po.ProductSecondPo" keyProperty="id" useGeneratedKeys="true">
+        INSERT INTO cm_second_hand_detail(
+			productID,
+			sold,
+			secondHandType,
+			instrumentType,
+			fixedYears,
+			maturityYears,
+			companyName,
+			detailTalkFlag,
+			originalPrice,
+			contactName,
+			contactMobile,
+			dockingPeopleName,
+			dockingPeopleMobile,
+			secondProductType,
+			townId,
+			brandName,
+			provinceCityDistrict,
+			address,
+			productQuality,
+			productDetails,
+			viewingNum,
+			payStatus,
+			payAmount,
+			payFormData,
+			payType,
+			payDate,
+			submitDate,
+			reviewedDate,
+			onLineDate,
+			source,
+			publisher
+		) VALUES (
+			#{productID},
+			#{sold},
+			#{secondHandType},
+			#{instrumentType},
+			#{fixedYears},
+			#{maturityYears},
+			#{companyName},
+			#{detailTalkFlag},
+			#{originalPrice},
+			#{contactName},
+			#{contactMobile},
+			#{dockingPeopleName},
+			#{dockingPeopleMobile},
+			#{secondProductType},
+			#{townId},
+			#{brandName},
+			#{provinceCityDistrict},
+			#{address},
+			#{productQuality},
+			#{productDetails},
+			#{viewingNum},
+			#{payStatus},
+			#{payAmount},
+			#{payFormData},
+			#{payType},
+			#{payDate},
+			#{submitDate},
+			#{reviewedDate},
+			#{onLineDate},
+			#{source},
+			#{publisher}
+		)
+    </insert>
+    <insert id="insertProductImage" parameterType="com.caimei365.commodity.model.po.ProductImagePo">
+        insert into productimage (productID, shopID, ADDTIME, image, mainFlag, sortIndex)
+        values (#{productId}, #{shopId}, #{addTime}, #{image}, #{mainFlag}, #{sortIndex})
+    </insert>
+    <insert id="insertProduct" keyColumn="productID" keyProperty="productId" parameterType="com.caimei365.commodity.model.po.ProductPo" useGeneratedKeys="true">
+
+    </insert>
     <select id="getSeconHandList" resultType="com.caimei365.commodity.model.vo.SecondListVo">
         select
             p.productID as productId,
@@ -68,5 +142,14 @@
         where productID = #{productId}
         order by mainFlag desc
     </select>
+    <select id="getAddressInfo" resultType="com.caimei365.commodity.model.vo.AddressVo">
+        select a.name as "province"
+        ,b.name as "city"
+        ,c.name as "town"
+        ,c.townId as "townID"
+        from province a
+        right join city b on a.provinceId = b.provinceId
+        right join town c on b.cityId = c.cityId where c.townId = #{townId}
+    </select>
 
 </mapper>