VoluntaryDto.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. package com.ruoyi.web.domain;
  2. import com.fasterxml.jackson.annotation.JsonFormat;
  3. import com.fasterxml.jackson.annotation.JsonIgnore;
  4. import com.google.common.collect.Maps;
  5. import com.google.common.collect.Sets;
  6. import com.ruoyi.common.utils.StringUtils;
  7. import com.ruoyi.syzy.domain.BBusiWishUniversities;
  8. import com.ruoyi.web.service.EnrollRateCalculator;
  9. import io.swagger.annotations.ApiModel;
  10. import io.swagger.annotations.ApiModelProperty;
  11. import lombok.AllArgsConstructor;
  12. import lombok.Data;
  13. import lombok.NoArgsConstructor;
  14. import org.apache.commons.compress.utils.Lists;
  15. import org.apache.commons.lang3.tuple.MutablePair;
  16. import org.apache.commons.lang3.tuple.Triple;
  17. import java.util.Date;
  18. import java.util.List;
  19. import java.util.Map;
  20. import java.util.Set;
  21. public class VoluntaryDto {
  22. @Data
  23. @ApiModel
  24. public static class VoluntaryRecord extends RenderRequest {
  25. Long id;
  26. String universityLogo;
  27. Long universityId;
  28. String universityName;
  29. Integer rank;
  30. List<VoluntaryMajorRecord> majors = Lists.newArrayList();
  31. }
  32. @Data
  33. @ApiModel
  34. public static class VoluntaryMajorRecord extends RenderRequest {
  35. Long majorId;
  36. String majorName;
  37. String majorAncestors;
  38. String majorGroup;
  39. Integer rank;
  40. }
  41. @Data
  42. @ApiModel
  43. public static class RenderRequest {
  44. @ApiModelProperty("0 render; 2. Skill")
  45. int renderType;
  46. @ApiModelProperty("院校专业")
  47. Long majorId;
  48. @ApiModelProperty("院校ID")
  49. Long universityId;
  50. }
  51. @Data
  52. @ApiModel
  53. @AllArgsConstructor
  54. @NoArgsConstructor
  55. public static class RenderRule {
  56. @ApiModelProperty("类型")
  57. String category;
  58. @ApiModelProperty("描述")
  59. String content;
  60. @ApiModelProperty("规则列表")
  61. List<AIRenderRule> details = Lists.newArrayList();
  62. }
  63. @Data
  64. @ApiModel
  65. public static class RenderMajorResult extends RenderRequest {
  66. @ApiModelProperty
  67. EnumPickType enumPickType;
  68. @ApiModelProperty
  69. Integer enrollRate; // 100进制,按原优志愿,为null表示无概率
  70. @ApiModelProperty
  71. String enrollRateText; // 概率描述,按原优志愿
  72. @ApiModelProperty
  73. List<RenderMajorHistory> histories = Lists.newArrayList();
  74. @ApiModelProperty
  75. RenderMajorSkill skill;
  76. }
  77. @Data
  78. @ApiModel
  79. public static class RenderMajorHistory {
  80. @ApiModelProperty
  81. Integer year; // 年份
  82. @ApiModelProperty
  83. String score; // 最低分
  84. @ApiModelProperty
  85. Integer plan; // 计划人数
  86. @ApiModelProperty
  87. Integer enroll; // 录取人数
  88. @ApiModelProperty
  89. Integer diff; // 负表示低于录取分;正数表示高于录取分
  90. @ApiModelProperty
  91. String ruleContent; // 完整的说明
  92. @ApiModelProperty
  93. String application; // 报名人数比值
  94. @ApiModelProperty
  95. String admission; // 计划人数比值
  96. }
  97. @Data
  98. @ApiModel
  99. public static class RenderMajorSkill {
  100. @ApiModelProperty
  101. Integer year; // 年份
  102. @ApiModelProperty
  103. String cultureScore; // 文化得分
  104. @ApiModelProperty
  105. String cultureRule; // 文化规则
  106. @ApiModelProperty
  107. String enrollScore; // 录取分
  108. @ApiModelProperty
  109. String skillScore; // 反向测技能分
  110. @ApiModelProperty
  111. String diff; // 负数表示低于skillScore,正数高于
  112. }
  113. // dynamic render rules
  114. // TODO: 部分规则不需要向前端返回:比如性别与考生类别,此类信息直接在当前用户中获取
  115. // 只需要向前端返回需要收集信息的部分
  116. @Data
  117. @ApiModel
  118. public static class AIRenderRule {
  119. // 类别;与AIResponse中的结论类型相对应;前会根据此类型分组展示表单,收集信息
  120. EnumRuleCategory enumRuleCategory;
  121. // 字段名,必填。前段会将收集的值以此名称返回
  122. // 请保持名称按一定的规则定义,如分数类型的由score开头等
  123. // 这样可以和原来定义的SingleRequest/MultipleRequest/AIRequest等吻合
  124. String fieldName;
  125. // 前端输入类型,见类型注释
  126. // 如果是分制,以filedName+`Total`返回分制选择结果
  127. EnumInputType enumInputType;
  128. // 校验规则
  129. // TODO: 先暂定前端自行生成,如果不行再考虑后台返回。
  130. // TODO: 前端会将录取规则设置为必填,其它规则设置为选填。如分制类输入,前端会将选中分制作为max校验。
  131. Boolean required;
  132. Integer min;
  133. Integer max;
  134. String regex;
  135. // 词典选项类选项 // 词典类选项的优先级最高
  136. String dictOptions;
  137. // 非词典选项 // 分制类规则,将多分制在此options中返回
  138. String[] options;
  139. // 对于多选时增加一个互斥选项
  140. String mutexOption;
  141. // 前端渲染字段
  142. String label; // 输入标题,必须填充
  143. String description; // 结尾描述文案,可选填充
  144. String placeholder; // 占位提示文案 // 选填,缺省前端会自行补充,后台返回的优先级高
  145. String tips; // 输入框下的特殊说明文案,可选填充
  146. // 特殊配置
  147. String defaultValue; // 缺省/初始化时的默认值
  148. Boolean dotDisable; // 数值输入时,是否带小数点,默认带
  149. String keyboardMode; // 当enumInputType==number时响应3种模式:number card car,默认number
  150. Boolean readonly;
  151. }
  152. @Data
  153. @ApiModel
  154. public static class AIRenderRequest {
  155. @ApiModelProperty("0 default; 1 AI。AI时职业技能输入得分率 2. 技能分计算")
  156. int renderType;
  157. @ApiModelProperty("一级专业大类")
  158. String majorCategory;
  159. @ApiModelProperty("二级专业细分集合")
  160. String[] majorTypes;
  161. @ApiModelProperty("三级专业集合")
  162. String[] majorCodes;
  163. @ApiModelProperty("专业招生代码")
  164. String[] majorEnrollCodes;
  165. @ApiModelProperty("学校")
  166. String universityCode;
  167. @ApiModelProperty("当前已经填写的表单信息")
  168. Map<String, String> form;
  169. }
  170. // 公共
  171. @Data
  172. @ApiModel
  173. public static class VoluntaryModel<I, O> {
  174. @ApiModelProperty("voluntaryType")
  175. EnumVoluntaryType voluntaryType;
  176. @ApiModelProperty("id")
  177. Long id;
  178. @ApiModelProperty("年份")
  179. Integer year;
  180. @ApiModelProperty("名称")
  181. String name;
  182. @ApiModelProperty("批次名")
  183. String batchName;
  184. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
  185. Date createTime;
  186. @ApiModelProperty("条件")
  187. I request; // 当前的填写信息,AIRequest.filter可以不保存后台自行决定
  188. @ApiModelProperty("用户")
  189. User userSnapshot; // 用户相关快照信息,因为有的计算条件是从用户信息上取的(这个后台自己存,前端不传)
  190. // 考虑传输节流,AIResponse.university保留需要快照的属性
  191. // 如:code,enrollCode // TODO 按需罗列(最好是前端只传university.code,后端来决定快照哪些信息)
  192. // 删除AIResponse.majorDetails.recommends // 志愿表中不需要它
  193. // 前端主动请求此model时,后台自动填充缺省信息
  194. @ApiModelProperty("详情")
  195. List<O> details; // 填报的院校专业
  196. }
  197. @Data
  198. public static class User {
  199. String name;
  200. String sex;
  201. String examType;
  202. String provinceName;
  203. }
  204. @Data
  205. @ApiModel
  206. public static class VoluntaryConfig {
  207. // 目前只有填报数量的配置,院校2个,专业4个
  208. @ApiModelProperty
  209. int collegeLimit = 2;
  210. @ApiModelProperty
  211. int majorLimit = 4;
  212. }
  213. // Request
  214. @Data
  215. @ApiModel
  216. public static class SingleRequest {
  217. @ApiModelProperty("院校代码")
  218. String universityCode;
  219. @ApiModelProperty("专业代码")
  220. String majorCode;
  221. @ApiModelProperty("专业招生代码")
  222. String majorEnrollCode;
  223. // 因为单选学校,所以不需要显式提供scoreSkillLimit
  224. @ApiModelProperty
  225. Map<String, String> form = Maps.newHashMap();
  226. }
  227. @Data
  228. @ApiModel
  229. public static class MultipleRequest {
  230. // TODO: 院校查询也需要支持此参数,来查询包含此专业大类的院校
  231. // TODO: 先暂定限制大类,可能需要限制专业细分2级分类
  232. @ApiModelProperty
  233. String majorCategory; // 因为多选时想尽量让规则重合,所以限制专业大类
  234. @ApiModelProperty
  235. List<CollegeMajorDto> universities; // 多个学校,先定2个,会受分数类型限制
  236. }
  237. @Data
  238. @ApiModel
  239. public static class AIRequest extends MultipleRequest {
  240. @ApiModelProperty
  241. Integer pageNum;
  242. @ApiModelProperty
  243. Integer pageSize;
  244. // 推荐列表筛选条件 // 筛选条件因为可以不保存,所以单独提取出来
  245. @ApiModelProperty
  246. AIRequestFilter filter = new AIRequestFilter();
  247. // TODO: 会尽快统计出来一些需要额外补充的信息
  248. @ApiModelProperty
  249. Map<String, String> form = Maps.newHashMap();
  250. }
  251. @Data
  252. @ApiModel
  253. public static class CollegeMajorDto {
  254. @ApiModelProperty
  255. String code; // university code
  256. @ApiModelProperty
  257. List<String> majorCodes; // major codes in university
  258. @ApiModelProperty
  259. List<String> majorEnrollCodes; // major enroll codes in university
  260. @ApiModelProperty
  261. Map<String, String> form = Maps.newHashMap();
  262. }
  263. @Data
  264. @ApiModel
  265. public static class AIRequestFilter {
  266. @ApiModelProperty
  267. List<String> majorTypes; // majorCode 二级分类
  268. @ApiModelProperty
  269. EnumPickType enumPickType; // 默认All
  270. @ApiModelProperty
  271. EnumPickEmpty enumPickEmpty; // 默认null, EnumPickType与EnumPickEmpty会互斥,一个传了另一个就会不传
  272. // TODO: 后台确认一下,如果A学校有的10个专业,有5个不满足条件,有3个有概率,2个没有概率。
  273. // 则展示时冲稳保展示A学校+3个有概率专业;无概率查询显示A学校+2个无概率专业
  274. @ApiModelProperty
  275. Boolean hasClearing; // true: 仅看补录 false:不看补录 null:不限。默认null
  276. // 院校通用筛选
  277. @ApiModelProperty
  278. String keyword; // 院校名称
  279. @ApiModelProperty
  280. List<String> level; // 办学层次
  281. @ApiModelProperty
  282. List<String> type; // 院校类型
  283. @ApiModelProperty
  284. List<String> natureTypeCN; // 办学类型
  285. @ApiModelProperty
  286. List<String> location; // 院校省份
  287. }
  288. public enum EnumPickType {
  289. /** 有概率类型 **/
  290. // TODO A 根据近3年有的录取分取平均值得到1个投档分
  291. // B 胡总给出一组浮动范围 +N-M分 属于有机会录取的,概率值可以先线性分布,也可以配比例尺
  292. // C B的配置需要做一套全局的,特定的学校可能会独立指定,因为头部学校和差学校的浮动范围肯定是不一样的
  293. All(""), // 全部
  294. Danger("冲"), // 冲
  295. Normal("稳"), // 稳
  296. Safety("保"); //
  297. String label;
  298. EnumPickType(String label) {
  299. this.label = label;
  300. }
  301. public String label() {
  302. return label;
  303. }
  304. }
  305. public enum EnumPickEmpty {
  306. /** 无概率类型 **/
  307. // 无概率:录取规则通过,特殊要求+专业要求不通过
  308. EnrollPass,
  309. // 新增/政策变动:无法计算录取规则
  310. // 如:没有往年录取数据
  311. New,
  312. }
  313. // Response
  314. @Data
  315. @ApiModel
  316. public static class SingleResponse {
  317. @ApiModelProperty
  318. String universityCode;
  319. @ApiModelProperty
  320. String majorCode;
  321. @ApiModelProperty
  322. String majorEnrollCode; // 专业招生代码
  323. @ApiModelProperty
  324. String majorGroup; // 专业组
  325. @ApiModelProperty
  326. String majorName; // 专业
  327. @ApiModelProperty
  328. String majorDirection;
  329. @ApiModelProperty
  330. EnumPickType enumPickType;
  331. @ApiModelProperty
  332. Integer enrollRate; // 100进制,按原优志愿,为null表示无概率
  333. @ApiModelProperty
  334. String enrollRateText; // 概率描述,按原优志愿
  335. @ApiModelProperty
  336. String tips; // 其它说明
  337. @ApiModelProperty
  338. List<MajorEnrollHistory> histories; // 历年数据
  339. @ApiModelProperty
  340. List<MajorClearingHistory> clearings; // 补录数据
  341. @ApiModelProperty
  342. List<MajorEnrollRule> rules; // 所有结论,前端可根据enumRuleType展示
  343. List<MajorEnrollRule> improves; // 各分项提升结论
  344. @JsonIgnore
  345. Double score;
  346. @JsonIgnore
  347. EnumPickEmpty enumPickEmpty;
  348. }
  349. // paging response
  350. @Data
  351. @ApiModel
  352. public static class AIResponse {
  353. @ApiModelProperty
  354. Integer enrollRate;
  355. @ApiModelProperty
  356. String enrollRateText;
  357. @ApiModelProperty
  358. EnumPickType enumPickType;
  359. @ApiModelProperty
  360. EnumPickEmpty enumPickEmpty;
  361. @ApiModelProperty
  362. String enrollCode; // 专业代码 TODO: 具体实现的时候看放在这里合不合适
  363. @ApiModelProperty
  364. BBusiWishUniversities university; // university base info.
  365. @ApiModelProperty
  366. List<SingleResponse> majorDetails; // major enroll rate details
  367. }
  368. @Data
  369. @ApiModel
  370. public static class MajorEnrollHistory {
  371. @ApiModelProperty
  372. Integer year; // 年份
  373. @ApiModelProperty
  374. String score; // Important: 返回差分 = myScore - historyScore; 返回'+N,-N'。以后可能返回真实分数
  375. @ApiModelProperty
  376. Integer plan; // 计划人数
  377. @ApiModelProperty
  378. Integer enroll; // 录取人数
  379. }
  380. @Data
  381. @ApiModel
  382. public static class MajorClearingHistory {
  383. @ApiModelProperty
  384. Integer year; // 年份
  385. @ApiModelProperty
  386. String score; // 同上,也暂时返回分差,返回'+N,-N'。
  387. @ApiModelProperty
  388. Integer realNum; // 补录人数
  389. }
  390. @Data
  391. @ApiModel
  392. public static class MajorEnrollRule {
  393. @ApiModelProperty
  394. EnumRuleCategory category; // 规则类别
  395. @ApiModelProperty
  396. EnumRuleType type; // 规则类型
  397. @ApiModelProperty
  398. String improveType; // 分类提升类型,随导入文件生成
  399. @ApiModelProperty
  400. String content; // 规则描述性文字
  401. @ApiModelProperty
  402. String description; // 有的规则会有额外的说明内容
  403. @ApiModelProperty
  404. Integer enrollRate; // 如果type=ScoreTotal,填充此字段
  405. @ApiModelProperty
  406. String enrollRateText; // 如果type=ScoreTotal,填充此字段
  407. @ApiModelProperty
  408. EnumPickType enumPickType;
  409. @ApiModelProperty
  410. Integer year;
  411. @ApiModelProperty
  412. String value; // 用户填写的值
  413. @ApiModelProperty
  414. Boolean valid; // true: 通过规则;false: 未通过规则; null: 未填写相关信息
  415. @ApiModelProperty
  416. String missingValue; // 差的数值: history.score-user.score。比如valid=false, 表示距离通过差的分值。
  417. @ApiModelProperty
  418. String failedMessage; // valid=false 或者 valid=null 时的文案说明
  419. @JsonIgnore
  420. EnrollRateCalculator.RateLevel rl;
  421. }
  422. public enum EnumInputType {
  423. Text, // 普通文本,一般情况下输入均使用此类。数值也可以使用此类型,结合校验规则使用
  424. Score, // AI的特殊输入类型,带分制的分数 // 此类输入之后可能涉及输入条件动态变化,NOTE:后面再考虑这种情况。
  425. Number, // 数值,会限制键盘。一般分数类、或者数值类的使用此类型。
  426. Radio, // radio单选。如果固定2个,用radio,否则用picker。一般超过3个会折行,影响美观
  427. Picker, // picker单选,一般使用Picker做单选。
  428. Eyesight,
  429. Checkbox // 多选
  430. // TODO:其它待发现
  431. }
  432. public enum EnumVoluntaryType {
  433. AI,
  434. Multiple
  435. }
  436. public enum EnumRuleCategory {
  437. None,
  438. Enroll, // 录取规则
  439. Special // 特殊要求
  440. }
  441. public enum EnumRuleType {
  442. None,
  443. // 1 总分,必须定义,有特殊展示
  444. // 2 学考与文化分,一般有特殊展示
  445. // 3 除学考与文化成绩外,录取规则涉及的分数,会有展示信息
  446. // 除以上3点外,均属于附加要求
  447. ScoreTotal, // 总分
  448. ScoreUnion, // 学考分,一般针对普高,为固定分,也有部分院校要求普高生也进行校考
  449. ScoreBase, // 文化分,校考,一般针对非普高
  450. ScoreSkill, // 技能分
  451. ScoreSingle, // 单科分
  452. Special, // 特殊要求, 包含判断的规则项
  453. Readonly, // 只读项不参与计算,仅在详情页展示
  454. other // 其它
  455. }
  456. public static class FormulaScoreStat {
  457. Set<String> validTotalSet = Sets.newHashSet("ScoreBase","ScoreSingle","ScoreSkill");
  458. Map<String, List<Triple<Double, Double, Double>>> groupItemListMap = Maps.newHashMap(); // 提分项目的 项目原始分 OriScore, 项目总分 Total,项目得分率 Rate
  459. Map<String, MutablePair<Double, Double>> groupTypeStatMap = Maps.newHashMap(); // 提分类型的 得分 Score,满分合计 Total
  460. Double groupAllTotal = 0.0; // 提分项满分合计
  461. Map<String, MutablePair<Double, Double>> typeKeyScoreMap = Maps.newHashMap(); // 类型项目的 得分 Score 及 原始分 OriScore
  462. Map<String, MutablePair<Double, Double>> typeScoreMap = Maps.newHashMap(); // 类型的 得分 Score 及 原始分 Total
  463. MutablePair<Double, Double> typeTotal = new MutablePair<>(0.0, 0.0); // 综合得分 及 原始分
  464. Integer typeAllTotal = 0; // 类型满分合计
  465. public boolean isValid() {
  466. return (typeScoreMap.containsKey("ScoreSingle") || typeScoreMap.containsKey("ScoreBase")) && typeScoreMap.containsKey("ScoreSkill");
  467. }
  468. public boolean isScoreValid() {
  469. return (typeScoreMap.containsKey("ScoreSingle") || typeScoreMap.containsKey("ScoreBase"));
  470. }
  471. public Integer getAllTotal() {
  472. return typeAllTotal;
  473. }
  474. /**
  475. * 按分类项统计
  476. * @param itemType ScoreSingle/ScoreBase, ScoreSkill
  477. * @param itemName Single对应科目
  478. * @param oriScore
  479. * @param rate
  480. * @return
  481. */
  482. public Double addType(String itemType, String itemName, Double oriScore, Double rate) {
  483. Double score = oriScore * rate;
  484. if (StringUtils.isBlank(itemType)) {
  485. return score;
  486. }
  487. merge(typeKeyScoreMap, itemType + "_" + itemName, score, oriScore); // 记录明细
  488. merge(typeScoreMap, itemType, score, oriScore); // 按类汇总
  489. if (validTotalSet.contains(itemType)) {
  490. typeTotal.setLeft(typeTotal.getLeft() + score); // 所有汇总
  491. typeTotal.setRight(typeTotal.getRight() + oriScore);
  492. }
  493. return score;
  494. }
  495. public void addTypeTotal(Integer total) {
  496. typeAllTotal += total;
  497. }
  498. public Double getTypeValue(String valueType, String itemName, Boolean isOri) {
  499. if ("StatSingle".equals(valueType)) { // 明细分
  500. String dataKey = "ScoreSingle_" + itemName;
  501. MutablePair<Double, Double> p = typeKeyScoreMap.get(dataKey);
  502. return null != p ? (isOri ? p.getRight() : p.getLeft()) : null;
  503. } else if ("StatSkill".equals(valueType)) { // 明细分
  504. String dataKey = "ScoreSkill_" + itemName;
  505. MutablePair<Double, Double> p = typeKeyScoreMap.get(dataKey);
  506. return null != p ? (isOri ? p.getRight() : p.getLeft()) : null;
  507. } else if ("StatCategoryScore".equals(valueType)) { // 分类分
  508. if(StringUtils.isBlank(itemName)) {
  509. return isOri ? typeTotal.getRight() : typeTotal.getLeft();
  510. }
  511. MutablePair<Double, Double> p = typeScoreMap.get(itemName);
  512. if(null == p) {
  513. p = typeScoreMap.get("ScoreBase".equals(itemName) ? "ScoreSingle" : "ScoreBase");
  514. }
  515. return null != p ? (isOri ? p.getRight() : p.getLeft()) : null;
  516. } else if ("StatRateScore".equals(valueType)) { // 综合分
  517. return isOri ? typeTotal.getRight() : typeTotal.getLeft();
  518. }
  519. return null;
  520. }
  521. /**
  522. * 按提分项统计 学考,综合测试
  523. * @param itemGroup
  524. * @param oriScore
  525. * @param total
  526. * @param rate
  527. * @return
  528. */
  529. public Double addGroup(String itemGroup, Double oriScore, Double total, Double rate) {
  530. Double rateScore = oriScore * rate;
  531. if (StringUtils.isBlank(itemGroup)) {
  532. return rateScore;
  533. }
  534. List<Triple<Double, Double, Double>> itemList = groupItemListMap.get(itemGroup);
  535. if (null == itemList) {
  536. itemList = Lists.newArrayList();
  537. groupItemListMap.put(itemGroup, itemList);
  538. }
  539. itemList.add(Triple.of(oriScore, total, rate));
  540. Double rateTotal = total * rate;
  541. merge(groupTypeStatMap, itemGroup, rateScore, rateTotal);
  542. groupAllTotal += rateTotal;
  543. return rateScore;
  544. }
  545. public List<MajorEnrollRule> getImproveScore(Double diffValue) {
  546. List<MajorEnrollRule> improvesList = Lists.newArrayList();
  547. if (groupTypeStatMap.containsKey("学考")) {
  548. MajorEnrollRule base = new MajorEnrollRule();
  549. base.setCategory(EnumRuleCategory.Enroll);
  550. base.setType(EnumRuleType.ScoreBase);
  551. base.setImproveType("学考");
  552. base.setMissingValue("0");
  553. improvesList.add(base);
  554. }
  555. for (String itemGroup : groupTypeStatMap.keySet()) {
  556. if ("学考".equals(itemGroup)) {
  557. continue;
  558. }
  559. MajorEnrollRule enrollRule = new MajorEnrollRule();
  560. enrollRule.setCategory(EnumRuleCategory.Enroll);
  561. enrollRule.setType("技能测试".equals(itemGroup) ? EnumRuleType.ScoreSkill : EnumRuleType.other);
  562. enrollRule.setImproveType(itemGroup);
  563. MutablePair<Double, Double> scorePair = groupTypeStatMap.get(itemGroup);
  564. Double missRateScore = diffValue * scorePair.getRight() / groupAllTotal;
  565. enrollRule.setMissingValue(String.valueOf(Math.round(getOriScore(groupItemListMap.get(itemGroup), missRateScore)))); // 差分 * 本类组总分/所有组总分
  566. improvesList.add(enrollRule);
  567. }
  568. return improvesList;
  569. }
  570. // 原始分, 总分, 得分率
  571. private Double getOriScore(List<Triple<Double, Double, Double>> itemList, Double missRateScore) {
  572. Double statTotal = itemList.stream().mapToDouble(t -> t.getMiddle()).sum();
  573. Double oriTotal = 0.0;
  574. for (Triple<Double, Double, Double> t : itemList) {
  575. oriTotal += Math.min(t.getMiddle() - t.getLeft(), missRateScore * t.getMiddle() / statTotal / t.getRight());
  576. }
  577. return Math.round(oriTotal * 10) / 10.0;
  578. }
  579. private void merge(Map<String, MutablePair<Double, Double>> pairMap, String key, Double value, Double oriValue) {
  580. MutablePair<Double, Double> exist = pairMap.get(key);
  581. if (null == exist) {
  582. exist = new MutablePair<>(value, oriValue);
  583. pairMap.put(key, exist);
  584. } else {
  585. exist.setLeft(exist.getLeft() + value);
  586. exist.setRight(exist.getRight() + oriValue);
  587. }
  588. }
  589. }
  590. }