Browse Source

二手风控

qrs 1 month ago
parent
commit
f014b6ce6a

+ 3 - 2
alien-entity/src/main/java/shop/alien/entity/second/SecondRiskControlRecord.java

@@ -68,9 +68,10 @@ public class SecondRiskControlRecord extends Model<SecondRiskControlRecord> {
 
     @TableField("delete_flag")
     @ApiModelProperty(value = "删除标记 0:未删除 1:已删除")
+    @TableLogic
     private Integer deleteFlag;
 
-    @TableField("created_time")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @ApiModelProperty(value = "创建时间")
     private Date createdTime;
@@ -79,7 +80,7 @@ public class SecondRiskControlRecord extends Model<SecondRiskControlRecord> {
     @ApiModelProperty(value = "创建人ID")
     private Integer createdUserId;
 
-    @TableField("updated_time")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @ApiModelProperty(value = "修改时间")
     private Date updatedTime;

+ 6 - 5
alien-entity/src/main/java/shop/alien/entity/second/SecondUserCredit.java

@@ -28,27 +28,28 @@ public class SecondUserCredit implements Serializable {
     private Integer userPoints;
 
     @TableField("delete_flag")
+    @TableLogic
     private Integer deleteFlag;
 
     @ApiModelProperty(value = "冻结时间")
     @TableField("freeze_time")
     private Date freezeTime;
 
-    @ApiModelProperty(value = "创建时间")
     @TableField(value = "created_time", fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "创建时间")
     private Date createdTime;
 
+    @TableField(value = "created_user_id", fill = FieldFill.INSERT)
     @ApiModelProperty(value = "创建人ID")
-    @TableField("created_user_id")
     private Integer createdUserId;
 
-    @ApiModelProperty(value = "修改时间")
-    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "修改时间")
     private Date updatedTime;
 
+    @TableField(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
     @ApiModelProperty(value = "修改人ID")
-    @TableField("updated_user_id")
     private Integer updatedUserId;
 }

+ 6 - 5
alien-entity/src/main/java/shop/alien/entity/second/SecondUserCreditRecord.java

@@ -31,23 +31,24 @@ public class SecondUserCreditRecord implements Serializable {
     private Integer pointsType;
 
     @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")
+    @ApiModelProperty(value = "创建时间")
     private Date createdTime;
 
+    @TableField(value = "created_user_id", fill = FieldFill.INSERT)
     @ApiModelProperty(value = "创建人ID")
-    @TableField("created_user_id")
     private Integer createdUserId;
 
-    @ApiModelProperty(value = "修改时间")
-    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "修改时间")
     private Date updatedTime;
 
+    @TableField(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
     @ApiModelProperty(value = "修改人ID")
-    @TableField("updated_user_id")
     private Integer updatedUserId;
 }

+ 8 - 0
alien-entity/src/main/java/shop/alien/mapper/second/SecondUserCreditMapper.java

@@ -1,12 +1,20 @@
 package shop.alien.mapper.second;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import shop.alien.entity.second.SecondUserCredit;
 
 
 /**
  * 二手商品映射器
  */
+@Mapper
 public interface SecondUserCreditMapper extends BaseMapper<SecondUserCredit> {
 
+    @Select("update second_user_credit set user_points = user_points + #{points} " +
+            "where user_id = #{userId} and delete_flag = 0 ")
+    void updatePointsByUserId(@Param("userId") int userId, @Param("points") int points);
+
 }

+ 19 - 0
alien-second/src/main/java/shop/alien/second/controller/SecondUserPointsController.java

@@ -1,5 +1,6 @@
 package shop.alien.second.controller;
 
+import com.alibaba.fastjson2.JSONObject;
 import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -78,4 +79,22 @@ public class SecondUserPointsController {
             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));
+    }
 }

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

@@ -1,5 +1,6 @@
 package shop.alien.second.service;
 
+import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.extension.service.IService;
 import shop.alien.entity.second.SecondUserCredit;
 
@@ -30,4 +31,8 @@ public interface SecondUserCreditService extends IService<SecondUserCredit> {
      * @return 是否创建成功
      */
     boolean createPointsRecord(Integer userId, Integer initialPoints, Integer pointsType);
+
+    boolean addIdInfoPoints(Integer userId) throws Exception;
+
+    JSONObject isFree(Integer userId) throws Exception;
 }

+ 60 - 8
alien-second/src/main/java/shop/alien/second/service/impl/SecondTradeRecordServiceImpl.java

@@ -1,18 +1,21 @@
 package shop.alien.second.service.impl;
 
+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.conditions.update.UpdateWrapper;
 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.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import shop.alien.config.properties.RiskControlProperties;
 import shop.alien.entity.result.BusinessException;
-import shop.alien.entity.second.SecondGoods;
-import shop.alien.entity.second.SecondGoodsRecord;
-import shop.alien.entity.second.SecondTradeOperation;
-import shop.alien.entity.second.SecondTradeRecord;
+import shop.alien.entity.second.*;
+import shop.alien.mapper.second.SecondRiskControlRecordMapper;
 import shop.alien.entity.second.vo.SecondTradeRecordVo;
 import shop.alien.entity.store.LifeMessage;
 import shop.alien.entity.store.LifeNotice;
@@ -23,10 +26,7 @@ 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.SecondGoodsMapper;
-import shop.alien.mapper.second.SecondGoodsRecordMapper;
-import shop.alien.mapper.second.SecondTradeOperationMapper;
-import shop.alien.mapper.second.SecondTradeRecordMapper;
+import shop.alien.mapper.second.*;
 import shop.alien.second.feign.AlienStoreFeign;
 import shop.alien.second.service.SecondTradeRecordService;
 import shop.alien.util.common.JwtUtil;
@@ -35,6 +35,7 @@ import java.time.*;
 import java.util.Date;
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -53,11 +54,16 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
     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;
+    @Autowired
+    private RiskControlProperties riskControlProperties;
 
     private final Object lock = new Object();
 
@@ -263,6 +269,26 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
                 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()) {
+                SecondRiskControlRecord riskControlRecord = new SecondRiskControlRecord();
+                riskControlRecord.setRuleType(1);
+                riskControlRecord.setUserId(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);
@@ -473,6 +499,12 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
             operation.setCreatedTime(new Date());
             secondTradeOperationMapper.insert(operation);
 
+            // 添加信用分
+            if (1 == type) {
+                creditScore(tradeRecord.getBuyerId());
+                creditScore(tradeRecord.getSellerId());
+            }
+
             return true;
         } catch (Exception e) {
             log.error("SecondTradeRecordServiceImpl.tradeCompleteConfirm(): Error Msg={}", e.getMessage());
@@ -480,6 +512,26 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
         }
     }
 
+    /**
+     * 增加交易信用分
+     * @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 {

+ 53 - 1
alien-second/src/main/java/shop/alien/second/service/impl/SecondUserCreditServiceImpl.java

@@ -1,13 +1,18 @@
 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.openqa.selenium.json.Json;
 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.mapper.second.SecondUserCreditMapper;
+import shop.alien.mapper.second.SecondUserCreditRecordMapper;
 import shop.alien.second.service.SecondUserCreditService;
 
 import java.util.Date;
@@ -21,8 +26,8 @@ import java.util.Date;
 public class SecondUserCreditServiceImpl extends ServiceImpl<SecondUserCreditMapper, SecondUserCredit> implements SecondUserCreditService {
     
     private final SecondUserCreditMapper secondUserCreditMapper;
-
     private final SecondUserCreditRecordServiceImpl serviceImpl;
+    private final SecondUserCreditRecordMapper secondUserCreditRecordMapper;
 
     /**
      * 根据用户ID获取用户积分信息
@@ -94,6 +99,53 @@ public class SecondUserCreditServiceImpl extends ServiceImpl<SecondUserCreditMap
         }
     }
 
+    @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);
+                secondUserCreditMapper.updatePointsByUserId(userId, 200);
+            }
+            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() == null || secondUserCredit.getUserPoints() < 500) {
+                jsonObject.put("flag", false);
+                jsonObject.put("msg", "信用分不足无法使用该功能");
+            } else if (secondUserCredit.getFreezeTime().after(new Date())) {
+                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 {
             // 创建新的积分记录