25 changed files with 616 additions and 223 deletions
@ -0,0 +1,39 @@
|
||||
package com.biutag.supervision.controller.sensitivePerception; |
||||
|
||||
import cn.hutool.core.util.StrUtil; |
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
import com.biutag.supervision.pojo.Result; |
||||
import com.biutag.supervision.pojo.entity.RiskPersonal; |
||||
import com.biutag.supervision.pojo.param.RiskPersonalQueryParam; |
||||
import com.biutag.supervision.service.RiskPersonalService; |
||||
import lombok.RequiredArgsConstructor; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
/** |
||||
* @author wxc |
||||
* @date 2024/11/12 |
||||
*/ |
||||
@Slf4j |
||||
@RequiredArgsConstructor |
||||
@RequestMapping("risk/personal") |
||||
@RestController |
||||
public class RiskPersonalController { |
||||
|
||||
private final RiskPersonalService riskPersonalService; |
||||
@GetMapping |
||||
public Result<Page<RiskPersonal>> list(RiskPersonalQueryParam param) { |
||||
LambdaQueryWrapper<RiskPersonal> queryWrapper = new LambdaQueryWrapper<>(); |
||||
queryWrapper.like(StrUtil.isNotBlank(param.getName()), RiskPersonal::getName, param.getName()) |
||||
.eq(StrUtil.isNotBlank(param.getGender()), RiskPersonal::getGender, param.getGender()) |
||||
.eq(Objects.nonNull(param.getAge()), RiskPersonal::getAge, param.getAge()) |
||||
.eq(StrUtil.isNotBlank(param.getControlDepartId()), RiskPersonal::getControlDepartId, param.getControlDepartId()); |
||||
return Result.success(riskPersonalService.page(Page.of(param.getCurrent(), param.getSize()), queryWrapper)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,8 @@
|
||||
package com.biutag.supervision.mapper; |
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
import com.biutag.supervision.pojo.entity.NegativeScoreDepart; |
||||
|
||||
public interface NegativeScoreDepartMapper extends BaseMapper<NegativeScoreDepart> { |
||||
|
||||
} |
||||
@ -0,0 +1,8 @@
|
||||
package com.biutag.supervision.mapper; |
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
import com.biutag.supervision.pojo.entity.RiskPersonal; |
||||
|
||||
public interface RiskPersonalMapper extends BaseMapper<RiskPersonal> { |
||||
|
||||
} |
||||
@ -0,0 +1,40 @@
|
||||
package com.biutag.supervision.pojo.entity; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import lombok.Getter; |
||||
import lombok.Setter; |
||||
|
||||
import java.time.LocalDateTime; |
||||
|
||||
@Setter |
||||
@Getter |
||||
public class NegativeScoreDepart { |
||||
|
||||
//
|
||||
@TableId(value = "negative_id") |
||||
private String negativeId; |
||||
|
||||
private String departId; |
||||
|
||||
//
|
||||
@TableField("discovery_time") |
||||
private LocalDateTime discoveryTime; |
||||
|
||||
//
|
||||
@TableField("create_time") |
||||
private LocalDateTime createTime; |
||||
|
||||
//
|
||||
@TableField("business_type_code") |
||||
private String businessTypeCode; |
||||
|
||||
//
|
||||
@TableField("score") |
||||
private Double score; |
||||
|
||||
//
|
||||
@TableField("expression") |
||||
private String expression; |
||||
|
||||
} |
||||
@ -0,0 +1,71 @@
|
||||
package com.biutag.supervision.pojo.entity; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import lombok.Getter; |
||||
import lombok.Setter; |
||||
|
||||
import java.time.LocalDateTime; |
||||
|
||||
@Setter |
||||
@Getter |
||||
public class RiskPersonal { |
||||
|
||||
// 主键
|
||||
@TableId(value = "id", type = IdType.AUTO) |
||||
private Integer id; |
||||
|
||||
// 姓名
|
||||
@TableField("name") |
||||
private String name; |
||||
|
||||
// 性别男1女2
|
||||
@TableField("gender") |
||||
private String gender; |
||||
|
||||
// 年龄
|
||||
@TableField("age") |
||||
private Integer age; |
||||
|
||||
// 大标签预览
|
||||
@TableField("tags") |
||||
private String tags; |
||||
|
||||
// 证件号码
|
||||
@TableField("id_code") |
||||
private String idCode; |
||||
|
||||
// 手机号
|
||||
@TableField("mobile_number") |
||||
private String mobileNumber; |
||||
|
||||
// 风险指数
|
||||
@TableField("risk_score") |
||||
private Integer riskScore; |
||||
|
||||
// 管控部门编号
|
||||
@TableField("control_depart_id") |
||||
private String controlDepartId; |
||||
|
||||
// 管控部门名称
|
||||
@TableField("control_depart_name") |
||||
private String controlDepartName; |
||||
|
||||
// 管控时间
|
||||
@TableField("control_time") |
||||
private LocalDateTime controlTime; |
||||
|
||||
// 创建时间
|
||||
@TableField("create_time") |
||||
private LocalDateTime createTime; |
||||
|
||||
// 异常信息
|
||||
@TableField("error_msg") |
||||
private String errorMsg; |
||||
|
||||
// 逻辑删除键0-未删1已删
|
||||
@TableField("del") |
||||
private Boolean del; |
||||
|
||||
} |
||||
@ -0,0 +1,27 @@
|
||||
package com.biutag.supervision.pojo.param; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.Setter; |
||||
|
||||
/** |
||||
* @author wxc |
||||
* @date 2024/11/12 |
||||
*/ |
||||
@Setter |
||||
@Getter |
||||
public class RiskPersonalQueryParam extends BasePage { |
||||
|
||||
private String name; |
||||
|
||||
// 性别男1女2
|
||||
private String gender; |
||||
|
||||
// 年龄
|
||||
private Integer age; |
||||
|
||||
// 手机号
|
||||
private String mobileNumber; |
||||
|
||||
private String controlDepartId; |
||||
|
||||
} |
||||
@ -0,0 +1,20 @@
|
||||
package com.biutag.supervision.service; |
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import com.biutag.supervision.mapper.NegativeScoreDepartMapper; |
||||
import com.biutag.supervision.pojo.entity.NegativeScoreDepart; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.util.Date; |
||||
import java.util.List; |
||||
|
||||
@Service |
||||
public class NegativeScoreDepartService extends ServiceImpl<NegativeScoreDepartMapper, NegativeScoreDepart> { |
||||
|
||||
public List<NegativeScoreDepart> list(Date beginTime, Date endTime) { |
||||
return list(new LambdaQueryWrapper<NegativeScoreDepart>() |
||||
.between(NegativeScoreDepart::getDiscoveryTime, beginTime, endTime)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,290 @@
|
||||
package com.biutag.supervision.service; |
||||
|
||||
import cn.hutool.core.bean.BeanUtil; |
||||
import cn.hutool.core.util.NumberUtil; |
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
import com.biutag.supervision.constants.enums.BusinessTypeEnum; |
||||
import com.biutag.supervision.constants.enums.NegativeLevelEnum; |
||||
import com.biutag.supervision.mapper.NegativeBlameMapper; |
||||
import com.biutag.supervision.mapper.SupDictContentMapper; |
||||
import com.biutag.supervision.pojo.entity.*; |
||||
import com.biutag.supervision.util.ScoreRule; |
||||
import lombok.RequiredArgsConstructor; |
||||
import org.springframework.scheduling.annotation.Async; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.time.LocalDateTime; |
||||
import java.util.*; |
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
import java.util.stream.Collectors; |
||||
|
||||
/** |
||||
* @author wxc |
||||
* @date 2024/11/12 |
||||
*/ |
||||
@RequiredArgsConstructor |
||||
@Service |
||||
public class NegativeScoreService { |
||||
|
||||
private final NegativeScorePoliceService negativeScorePoliceService; |
||||
|
||||
private final NegativeScoreDepartService negativeScoreDepartService; |
||||
|
||||
private final NegativeBlameMapper blameMapper; |
||||
|
||||
private final NegativeBlameService blameService; |
||||
|
||||
private final NegativeService negativeService; |
||||
|
||||
private final NegativeProblemRelationService problemRelationService; |
||||
|
||||
private final SupDictContentMapper supDictContentMapper; |
||||
|
||||
private AtomicBoolean flag = new AtomicBoolean(false); |
||||
|
||||
public void updateNegativeScore() { |
||||
if (flag.get()) { |
||||
throw new RuntimeException("风险指数正在计算中"); |
||||
} |
||||
flag.set(true); |
||||
System.out.println("calculateScore-------------------------------------------------"); |
||||
List<NegativeBlame> negativeBlames = blameMapper.selectVerifyTrue(); |
||||
String formula = "%s + (%s * %s) + (%s * %s)"; |
||||
List<NegativeScoreDepart> scoreDeparts = new ArrayList<>(); |
||||
negativeScorePoliceService.remove(new LambdaQueryWrapper<>()); |
||||
negativeScoreDepartService.remove(new LambdaQueryWrapper<>()); |
||||
negativeBlames.forEach(blame -> { |
||||
Negative negative = negativeService.getById(blame.getNegativeId()); |
||||
NegativeScorePolice negativeScorePolice = new NegativeScorePolice() |
||||
.setNegativeId(blame.getNegativeId()) |
||||
.setIdCode(blame.getBlameIdCode()) |
||||
.setDiscoveryTime(negative.getDiscoveryTime()) |
||||
.setIdCode(blame.getBlameIdCode()) |
||||
.setBusinessTypeCode(negative.getBusinessTypeCode()) |
||||
.setCreateTime(LocalDateTime.now()); |
||||
List<NegativeProblemRelation> problems = problemRelationService.list(blame.getNegativeId(), blame.getBlameId()); |
||||
List<String> codes = problems.stream().map(NegativeProblemRelation::getThreeLevelCode).toList(); |
||||
if (codes.isEmpty()) { |
||||
return; |
||||
} |
||||
SupDictProblemType problemType = supDictContentMapper.selectOneByMaxScore(codes); |
||||
LocalDateTime endTime = negative.getDiscoveryTime(); |
||||
LocalDateTime beginTime = endTime.minusYears(1); |
||||
int frequency = blameService.countByTrue(blame.getBlameIdCode(), problemType.getParentCode(), beginTime, endTime); |
||||
String expression = String.format(formula, |
||||
problemType.getScore(), |
||||
problemType.getScore(), |
||||
NegativeLevelEnum.GENERAL_IMPACT.getScore(), |
||||
problemType.getScore(), |
||||
ScoreRule.getFrequencyScore(frequency)); |
||||
|
||||
double calculate = NumberUtil.calculate(expression); |
||||
double score = NumberUtil.roundHalfEven(calculate, 2).doubleValue(); |
||||
negativeScorePolice.setScore(score) |
||||
.setExpression(expression); |
||||
negativeScorePoliceService.save(negativeScorePolice); |
||||
NegativeScoreDepart scoreDepart = scoreDeparts.stream().filter(item -> item.getNegativeId().equals(negative.getId())).findFirst().orElse(null); |
||||
if (Objects.isNull(scoreDepart)) { |
||||
scoreDepart = new NegativeScoreDepart(); |
||||
BeanUtil.copyProperties(negativeScorePolice, scoreDepart); |
||||
scoreDepart.setDepartId(negative.getInvolveDepartId()); |
||||
scoreDeparts.add(scoreDepart); |
||||
} else if (scoreDepart.getScore() < score) { |
||||
scoreDepart.setScore(score); |
||||
} |
||||
}); |
||||
if (!scoreDeparts.isEmpty()) { |
||||
negativeScoreDepartService.saveBatch(scoreDeparts); |
||||
} |
||||
flag.set(false); |
||||
System.out.println("calculateScore-------------------------------------------------End"); |
||||
} |
||||
|
||||
|
||||
private final BusinessPoliceService businessPoliceService; |
||||
|
||||
private final BusinessDepartService businessDepartService; |
||||
|
||||
private final SupPoliceService policeService; |
||||
|
||||
// 业务权重 = 单个业务风险指数 / 总业务风险指数之和
|
||||
public double calculateBusinessWeight(Double totalScore, Double score) { |
||||
if (totalScore == 0) { |
||||
return 0.0; |
||||
} |
||||
|
||||
return NumberUtil.div(score, totalScore); |
||||
} |
||||
|
||||
// 平均问题数 = 单个业务问题数 / 业务涉及人数
|
||||
public double calculateAvgNumber(int size, int policeSize) { |
||||
if (policeSize == 0) { |
||||
return 0; |
||||
} |
||||
return NumberUtil.div(size, policeSize); |
||||
} |
||||
|
||||
// 业务标准差 = 根号(sum((个人问题数 - 平均问题数)²) / 业务涉及人数)
|
||||
public double calculateSd(double[] diffNumber, int policeSize) { |
||||
if (policeSize == 0) { |
||||
return 0; |
||||
} |
||||
return Math.sqrt(Arrays.stream(diffNumber).map(val -> Math.pow(val, 2)).sum() / policeSize); |
||||
} |
||||
|
||||
// 业务差异值1 = (个人问题发生率 - 平均问题发生率) / 业务标准差
|
||||
// 平均问题发生率 = 总问题数 / 总业务量
|
||||
// 个人问题发生率 = 个人问题数 / 个人业务量
|
||||
public double calculateDiff1(List<NegativeScorePolice> businessScorePolice, SupPolice police, Date beginTime, Date endTime, String businessTypeCode, double sd) { |
||||
if (sd == 0) { |
||||
return 0; |
||||
} |
||||
List<BusinessPolice> businessPolices = businessPoliceService.list(beginTime, endTime, businessTypeCode); |
||||
// 总业务量
|
||||
int totalBusinessSize = businessPolices.stream().mapToInt(BusinessPolice::getNumber).sum(); |
||||
// 平均问题发生率
|
||||
double avgRate = totalBusinessSize == 0? 0: NumberUtil.div(businessScorePolice.size(), totalBusinessSize); |
||||
// 个人问题数
|
||||
long personSize = businessScorePolice.stream().filter(item -> police.getIdCode().equals(item.getIdCode())).count(); |
||||
// 个人业务量
|
||||
long personBusinessSize = businessPolices.stream().filter(item -> police.getName().equals(item.getPoliceName()) && police.getEmpNo().equals(item.getEmpNo())).count(); |
||||
// 个人问题发生率
|
||||
double personRate = personBusinessSize == 0? 0: NumberUtil.div(personSize, personBusinessSize); |
||||
return NumberUtil.div(personRate - avgRate, sd); |
||||
} |
||||
|
||||
public double calculateDiff1ByDepart(List<NegativeScoreDepart> businessScoreDeparts, String departId, Date beginTime, Date endTime, String businessTypeCode, double sd) { |
||||
if (sd == 0) { |
||||
return 0; |
||||
} |
||||
List<BusinessDepart> businessDeparts = businessDepartService.list(beginTime, endTime, businessTypeCode); |
||||
// 总业务量
|
||||
int totalBusinessSize = businessDeparts.stream().mapToInt(BusinessDepart::getNumber).sum(); |
||||
// 平均问题发生率
|
||||
double avgRate = totalBusinessSize == 0? 0: NumberUtil.div(businessScoreDeparts.size(), totalBusinessSize); |
||||
// 个人问题数
|
||||
long personSize = businessScoreDeparts.stream().filter(item -> departId.equals(item.getDepartId())).count(); |
||||
// 个人业务量
|
||||
long personBusinessSize = businessDeparts.stream().filter(item -> departId.equals(item.getDepartId())).count(); |
||||
// 个人问题发生率
|
||||
double personRate = personBusinessSize == 0? 0: NumberUtil.div(personSize, personBusinessSize); |
||||
return NumberUtil.div(personRate - avgRate, sd); |
||||
} |
||||
|
||||
// 业务差异值2 = (问题数 - 平均问题数) / 业务标准差
|
||||
public double calculateDiff2(long personalSize, double avgNumber, double sd) { |
||||
if (sd == 0) { |
||||
return 0; |
||||
} |
||||
return NumberUtil.div(personalSize - avgNumber, sd); |
||||
} |
||||
|
||||
public double calculateBusinessScore(double diff, long personalSize) { |
||||
if (personalSize == 0) { |
||||
return 0; |
||||
} |
||||
return 50 + (diff * 15); |
||||
} |
||||
|
||||
// 平均问题数 = 单个业务问题数 / 业务涉及人数
|
||||
// 业务标准差 = sum((个人问题数 - 平均问题数)²) / 业务涉及人数
|
||||
// 业务差异值1 = (问题发生率 - 平均问题发生率) / 业务标准差
|
||||
// 业务差异值2 = (问题数 - 平均问题数) / 业务标准差
|
||||
// 业务风险指数 = 50 + (业务差异值 * 15)
|
||||
// 业务权重 = 单个业务风险指数 / 总业务风险指数之和
|
||||
// 个人风险指数 = sum(业务风险指数 * 业务权重)
|
||||
|
||||
/** |
||||
* 个人风险指数 |
||||
* @param beginTime |
||||
* @param endTime |
||||
* @param idCode |
||||
* @return |
||||
*/ |
||||
public List<Object> calculatePoliceScore(Date beginTime, Date endTime, String idCode) { |
||||
List<NegativeScorePolice> scorePolices = negativeScorePoliceService.list(beginTime, endTime); |
||||
Double totalScore = scorePolices.stream().mapToDouble(NegativeScorePolice::getScore).sum(); |
||||
SupPolice police = policeService.getByIdCode(idCode); |
||||
List<String> expressionArr = new ArrayList<>(); |
||||
double policeScore = Arrays.stream(BusinessTypeEnum.values()).mapToDouble(businessTypeEnum -> { |
||||
List<NegativeScorePolice> businessScorePolice = scorePolices.stream().filter(item -> item.getBusinessTypeCode().equals(businessTypeEnum.getValue())).toList(); |
||||
// 业务涉及人数
|
||||
int policeSize = businessScorePolice.stream().map(NegativeScorePolice::getIdCode).collect(Collectors.toSet()).size(); |
||||
// 业务问题数
|
||||
int businessSize = businessScorePolice.size(); |
||||
//---------------------------------------------------------
|
||||
// 平均问题数
|
||||
double avgNumber = calculateAvgNumber(businessSize, policeSize); |
||||
// 差值集合 = 个人问题数 - 平均问题数
|
||||
Map<String, List<NegativeScorePolice>> group = businessScorePolice.stream().collect(Collectors.groupingBy(NegativeScorePolice::getIdCode)); |
||||
double[] diffNumbers = group.values().stream().mapToDouble(val -> val.size() - avgNumber).toArray(); |
||||
// 业务标准差
|
||||
double sd = calculateSd(diffNumbers, policeSize); |
||||
// 业务差异值
|
||||
double diff; |
||||
long personalSize = businessScorePolice.stream().filter(item -> idCode.equals(item.getIdCode())).count(); |
||||
if (businessTypeEnum.getBusinessVolume()) { |
||||
diff = calculateDiff1(businessScorePolice, police, beginTime, endTime, businessTypeEnum.getValue(), sd); |
||||
} else { |
||||
diff = calculateDiff2(personalSize, avgNumber, sd); |
||||
} |
||||
// 业务风险指数
|
||||
double businessScore = calculateBusinessScore(diff, personalSize); |
||||
// 业务权重
|
||||
Double score = businessScorePolice.stream().mapToDouble(NegativeScorePolice::getScore).sum(); |
||||
double businessWeight = calculateBusinessWeight(totalScore, score); |
||||
expressionArr.add(String.format("(%s * %s)", businessScore, businessWeight)); |
||||
return NumberUtil.mul(businessScore, businessWeight); |
||||
}).sum(); |
||||
BigDecimal score = NumberUtil.roundHalfEven(policeScore, 2); |
||||
String expression = String.join(" + ", expressionArr); |
||||
return List.of(score, expression); |
||||
} |
||||
|
||||
/** |
||||
* 单位风险指数 |
||||
* @param beginTime |
||||
* @param endTime |
||||
* @param departId |
||||
* @return |
||||
*/ |
||||
|
||||
public List<Object> calculateDepartScore(Date beginTime, Date endTime, String departId) { |
||||
List<NegativeScoreDepart> scoreDeparts = negativeScoreDepartService.list(beginTime, endTime); |
||||
Double totalScore = scoreDeparts.stream().mapToDouble(NegativeScoreDepart::getScore).sum(); |
||||
List<String> expressionArr = new ArrayList<>(); |
||||
double policeScore = Arrays.stream(BusinessTypeEnum.values()).mapToDouble(businessTypeEnum -> { |
||||
List<NegativeScoreDepart> businessScoreDeparts = scoreDeparts.stream().filter(item -> item.getBusinessTypeCode().equals(businessTypeEnum.getValue())).toList(); |
||||
// 业务涉及人数
|
||||
int departSize = businessScoreDeparts.stream().map(NegativeScoreDepart::getDepartId).collect(Collectors.toSet()).size(); |
||||
// 当前部门问题数
|
||||
long personalSize = businessScoreDeparts.stream().filter(item -> departId.equals(item.getDepartId())).count(); |
||||
// 平均问题数
|
||||
double avgNumber = calculateAvgNumber(businessScoreDeparts.size(), departSize); |
||||
// 差值集合 = 个人问题数 - 平均问题数
|
||||
Map<String, List<NegativeScoreDepart>> group = businessScoreDeparts.stream().collect(Collectors.groupingBy(NegativeScoreDepart::getDepartId)); |
||||
double[] diffNumbers = group.values().stream().mapToDouble(val -> val.size() - avgNumber).toArray(); |
||||
// 业务标准差
|
||||
double sd = calculateSd(diffNumbers, departSize); |
||||
// 业务差异值
|
||||
double diff; |
||||
if (businessTypeEnum.getBusinessVolume()) { |
||||
diff = calculateDiff1ByDepart(businessScoreDeparts, departId, beginTime, endTime, businessTypeEnum.getValue(), sd); |
||||
} else { |
||||
diff = calculateDiff2(personalSize, avgNumber, sd); |
||||
} |
||||
// 业务风险指数
|
||||
double businessScore = calculateBusinessScore(diff, personalSize); |
||||
// 业务权重
|
||||
Double score = businessScoreDeparts.stream().mapToDouble(NegativeScoreDepart::getScore).sum(); |
||||
double businessWeight = calculateBusinessWeight(totalScore, score); |
||||
expressionArr.add(String.format("(%s * %s)", businessScore, businessWeight)); |
||||
return NumberUtil.mul(businessScore, businessWeight); |
||||
}).sum(); |
||||
BigDecimal score = NumberUtil.roundHalfEven(policeScore, 2); |
||||
String expression = String.join(" + ", expressionArr); |
||||
return List.of(score, expression); |
||||
} |
||||
|
||||
} |
||||
@ -1,138 +0,0 @@
|
||||
package com.biutag.supervision.service; |
||||
|
||||
import cn.hutool.core.util.NumberUtil; |
||||
import com.biutag.supervision.constants.enums.BusinessTypeEnum; |
||||
import com.biutag.supervision.pojo.entity.BusinessPolice; |
||||
import com.biutag.supervision.pojo.entity.NegativeScorePolice; |
||||
import com.biutag.supervision.pojo.entity.SupPolice; |
||||
import lombok.RequiredArgsConstructor; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.util.*; |
||||
import java.util.stream.Collectors; |
||||
|
||||
/** |
||||
* |
||||
* @author wxc |
||||
* @date 2024/11/11 |
||||
*/ |
||||
// 平均问题数 = 总问题数 / 业务涉及人数
|
||||
// 业务标准差 = sum((个人问题数 - 平均问题数)²) / 业务涉及人数
|
||||
// 业务差异值1 = (问题发生率 - 平均问题发生率) / 业务标准差
|
||||
// 业务差异值2 = (问题数 - 平均问题数) / 业务标准差
|
||||
// 业务风险指数 = 50 + (业务差异值 * 15)
|
||||
// 业务权重 = 单个业务风险指数 / 总业务风险指数之和
|
||||
// 个人风险指数 = sum(业务风险指数 * 业务权重)
|
||||
@RequiredArgsConstructor |
||||
@Service |
||||
public class ProfilePoliceService { |
||||
|
||||
private final NegativeScorePoliceService negativeScorePoliceService; |
||||
|
||||
private final BusinessPoliceService businessPoliceService; |
||||
|
||||
private final SupPoliceService policeService; |
||||
|
||||
// 业务权重 = 单个业务风险指数 / 总业务风险指数之和
|
||||
public double getBusinessWeight(Double totalScore, List<NegativeScorePolice> businessScorePolice) { |
||||
if (totalScore == 0) { |
||||
return 0.0; |
||||
} |
||||
Double score = businessScorePolice.stream().mapToDouble(NegativeScorePolice::getScore).sum(); |
||||
return NumberUtil.div(score, totalScore); |
||||
} |
||||
|
||||
// 平均问题数 = 单个业务问题数 / 业务涉及人数
|
||||
public double getAvgNumber(int size, int policeSize) { |
||||
if (policeSize == 0) { |
||||
return 0; |
||||
} |
||||
return NumberUtil.div(size, policeSize); |
||||
} |
||||
|
||||
// 业务标准差 = 根号(sum((个人问题数 - 平均问题数)²) / 业务涉及人数)
|
||||
public double getSd(List<NegativeScorePolice> businessScorePolice, double avgNumber, int policeSize) { |
||||
if (policeSize == 0) { |
||||
return 0; |
||||
} |
||||
Map<String, List<NegativeScorePolice>> group = businessScorePolice.stream().collect(Collectors.groupingBy(NegativeScorePolice::getIdCode)); |
||||
return Math.sqrt(group.values().stream().mapToDouble(val -> Math.pow(val.size() - avgNumber, 2)).sum() / policeSize); |
||||
} |
||||
|
||||
// 业务差异值1 = (个人问题发生率 - 平均问题发生率) / 业务标准差
|
||||
// 平均问题发生率 = 总问题数 / 总业务量
|
||||
// 个人问题发生率 = 个人问题数 / 个人业务量
|
||||
public double getDiff1(List<NegativeScorePolice> businessScorePolice, SupPolice police, Date beginTime, Date endTime, String businessTypeCode, double sd) { |
||||
if (sd == 0) { |
||||
return 0; |
||||
} |
||||
List<BusinessPolice> businessPolices = businessPoliceService.list(beginTime, endTime, businessTypeCode); |
||||
// 总业务量
|
||||
int totalBusinessSize = businessPolices.stream().mapToInt(BusinessPolice::getNumber).sum(); |
||||
// 平均问题发生率
|
||||
double avgRate = totalBusinessSize == 0? 0: NumberUtil.div(businessScorePolice.size(), totalBusinessSize); |
||||
// 个人问题数
|
||||
long personSize = businessScorePolice.stream().filter(item -> police.getIdCode().equals(item.getIdCode())).count(); |
||||
// 个人业务量
|
||||
long personBusinessSize = businessPolices.stream().filter(item -> police.getName().equals(item.getPoliceName()) && police.getEmpNo().equals(item.getEmpNo())).count(); |
||||
// 个人问题发生率
|
||||
double personRate = personBusinessSize == 0? 0: NumberUtil.div(personSize, personBusinessSize); |
||||
return NumberUtil.div(personRate - avgRate, sd); |
||||
} |
||||
|
||||
// 业务差异值2 = (问题数 - 平均问题数) / 业务标准差
|
||||
public double getDiff2(List<NegativeScorePolice> businessScorePolice, String idCode, double avgNumber, double sd) { |
||||
if (sd == 0) { |
||||
return 0; |
||||
} |
||||
long size = businessScorePolice.stream().filter(item -> idCode.equals(item.getIdCode())).count(); |
||||
return NumberUtil.div(size - avgNumber, sd); |
||||
} |
||||
|
||||
|
||||
public double getBusinessScore(double diff) { |
||||
return 50 + (diff * 15); |
||||
} |
||||
|
||||
|
||||
// 平均问题数 = 单个业务问题数 / 业务涉及人数
|
||||
// 业务标准差 = sum((个人问题数 - 平均问题数)²) / 业务涉及人数
|
||||
// 业务差异值1 = (问题发生率 - 平均问题发生率) / 业务标准差
|
||||
// 业务差异值2 = (问题数 - 平均问题数) / 业务标准差
|
||||
// 业务风险指数 = 50 + (业务差异值 * 15)
|
||||
// 业务权重 = 单个业务风险指数 / 总业务风险指数之和
|
||||
// 个人风险指数 = sum(业务风险指数 * 业务权重)
|
||||
public List<Object> getPoliceScore(Date beginTime, Date endTime, String idCode) { |
||||
List<NegativeScorePolice> scorePolices = negativeScorePoliceService.list(beginTime, endTime); |
||||
Double totalScore = scorePolices.stream().mapToDouble(NegativeScorePolice::getScore).sum(); |
||||
SupPolice police = policeService.getByIdCode(idCode); |
||||
List<String> expressionArr = new ArrayList<>(); |
||||
double policeScore = Arrays.stream(BusinessTypeEnum.values()).mapToDouble(businessTypeEnum -> { |
||||
List<NegativeScorePolice> businessScorePolice = scorePolices.stream().filter(item -> item.getBusinessTypeCode().equals(businessTypeEnum.getValue())).toList(); |
||||
// 业务涉及人数
|
||||
int policeSize = businessScorePolice.stream().map(NegativeScorePolice::getIdCode).collect(Collectors.toSet()).size(); |
||||
// 平均问题数
|
||||
double avgNumber = getAvgNumber(businessScorePolice.size(), policeSize); |
||||
// 业务标准差
|
||||
double sd = getSd(businessScorePolice, avgNumber, policeSize); |
||||
// 业务差异值
|
||||
double diff; |
||||
if (businessTypeEnum.getBusinessVolume()) { |
||||
diff = getDiff1(businessScorePolice, police, beginTime, endTime, businessTypeEnum.getValue(), sd); |
||||
} else { |
||||
diff = getDiff2(businessScorePolice, idCode, avgNumber, sd); |
||||
} |
||||
// 业务风险指数
|
||||
double businessScore = getBusinessScore(diff); |
||||
// 业务权重
|
||||
double businessWeight = getBusinessWeight(totalScore, businessScorePolice); |
||||
expressionArr.add(String.format("(%s * %s)", businessScore, businessWeight)); |
||||
return NumberUtil.mul(businessScore, businessWeight); |
||||
}).sum(); |
||||
BigDecimal score = NumberUtil.roundHalfEven(policeScore, 2); |
||||
String expression = String.join(" + ", expressionArr); |
||||
return List.of(score, expression); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,11 @@
|
||||
package com.biutag.supervision.service; |
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import com.biutag.supervision.pojo.entity.RiskPersonal; |
||||
import com.biutag.supervision.mapper.RiskPersonalMapper; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
@Service |
||||
public class RiskPersonalService extends ServiceImpl<RiskPersonalMapper, RiskPersonal> { |
||||
|
||||
} |
||||
@ -0,0 +1,33 @@
|
||||
package com.biutag.supervision.service; |
||||
|
||||
import cn.hutool.core.util.NumberUtil; |
||||
import com.biutag.supervision.constants.enums.BusinessTypeEnum; |
||||
import com.biutag.supervision.pojo.entity.BusinessPolice; |
||||
import com.biutag.supervision.pojo.entity.NegativeScorePolice; |
||||
import com.biutag.supervision.pojo.entity.SupPolice; |
||||
import lombok.RequiredArgsConstructor; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.util.*; |
||||
import java.util.stream.Collectors; |
||||
|
||||
/** |
||||
* |
||||
* @author wxc |
||||
* @date 2024/11/11 |
||||
*/ |
||||
// 平均问题数 = 总问题数 / 业务涉及人数
|
||||
// 业务标准差 = sum((个人问题数 - 平均问题数)²) / 业务涉及人数
|
||||
// 业务差异值1 = (问题发生率 - 平均问题发生率) / 业务标准差
|
||||
// 业务差异值2 = (问题数 - 平均问题数) / 业务标准差
|
||||
// 业务风险指数 = 50 + (业务差异值 * 15)
|
||||
// 业务权重 = 单个业务风险指数 / 总业务风险指数之和
|
||||
// 个人风险指数 = sum(业务风险指数 * 业务权重)
|
||||
@RequiredArgsConstructor |
||||
@Service |
||||
public class ScoreService { |
||||
|
||||
|
||||
|
||||
} |
||||
Loading…
Reference in new issue