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