VoluntaryService.java 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172
  1. package com.ruoyi.web.service;
  2. import com.alibaba.fastjson2.JSONArray;
  3. import com.alibaba.fastjson2.JSONException;
  4. import com.alibaba.fastjson2.JSONObject;
  5. import com.fasterxml.jackson.core.JsonProcessingException;
  6. import com.fasterxml.jackson.core.type.TypeReference;
  7. import com.fasterxml.jackson.databind.DeserializationFeature;
  8. import com.fasterxml.jackson.databind.ObjectMapper;
  9. import com.google.common.collect.Lists;
  10. import com.google.common.collect.Maps;
  11. import com.google.common.collect.Sets;
  12. import com.ruoyi.common.core.domain.R;
  13. import com.ruoyi.common.core.domain.entity.SysUser;
  14. import com.ruoyi.common.core.page.TableDataInfo;
  15. import com.ruoyi.common.exception.ServiceException;
  16. import com.ruoyi.common.utils.SecurityUtils;
  17. import com.ruoyi.common.utils.StringUtils;
  18. import com.ruoyi.ie.domain.*;
  19. import com.ruoyi.ie.mapper.*;
  20. import com.ruoyi.system.service.ISysConfigService;
  21. import com.ruoyi.syzy.domain.BBusiWishUniversities;
  22. import com.ruoyi.syzy.domain.BBusiWishUniversitiesProfession;
  23. import com.ruoyi.syzy.dto.UniversityDetailDTO;
  24. import com.ruoyi.syzy.mapper.BBusiWishRecordsMapper;
  25. import com.ruoyi.syzy.mapper.BBusiWishUniversitiesMapper;
  26. import com.ruoyi.syzy.mapper.BBusiWishUniversitiesProfessionMapper;
  27. import com.ruoyi.util.PageUtil;
  28. import com.ruoyi.web.domain.Constant;
  29. import com.ruoyi.web.domain.VoluntaryDto;
  30. import io.swagger.annotations.ApiParam;
  31. import org.apache.commons.collections.CollectionUtils;
  32. import org.apache.commons.collections.MapUtils;
  33. import org.apache.commons.lang3.ArrayUtils;
  34. import org.apache.commons.lang3.math.NumberUtils;
  35. import org.apache.commons.lang3.tuple.MutablePair;
  36. import org.apache.commons.lang3.tuple.Pair;
  37. import org.springframework.stereotype.Service;
  38. import org.springframework.web.bind.annotation.RequestParam;
  39. import java.text.SimpleDateFormat;
  40. import java.util.*;
  41. import java.util.function.Function;
  42. import java.util.stream.Collectors;
  43. @Service
  44. public class VoluntaryService {
  45. private ObjectMapper mapper = new ObjectMapper();
  46. private static TypeReference<List<VoluntaryDto.SingleResponse>> wishDetailsTypeReference = new TypeReference<List<VoluntaryDto.SingleResponse>>() { };
  47. private static TypeReference<List<VoluntaryDto.AIResponse>> wishAiDetailsTypeReference = new TypeReference<List<VoluntaryDto.AIResponse>>() { };
  48. Set<String> NumberTypeSet = Sets.newHashSet(VoluntaryDto.EnumInputType.Number.name(), VoluntaryDto.EnumInputType.Eyesight.name(), VoluntaryDto.EnumInputType.Score.name());
  49. private final AWishRecordMapper aWishRecordMapper;
  50. private final AEnrollScoreMapper aEnrollScoreMapper;
  51. private final AEnrollSpecialMapper aEnrollSpecialMapper;
  52. private final AMarjorPlanMapper aMarjorPlanMapper;
  53. private final AMarjorSubmitMapper aMarjorSubmitMapper;
  54. private final AEnrollUniversityMapper aEnrollUniversityMapper;
  55. private final BBusiWishUniversitiesMapper bBusiWishUniversitiesMapper;
  56. private final BBusiWishUniversitiesProfessionMapper bBusiWishUniversitiesProfessionMapper;
  57. private final ISysConfigService sysConfigService;
  58. private final EnrollRateCalculator enrollRateCalculator;
  59. private final BBusiWishRecordsMapper busiWishRecordsMapper;
  60. public VoluntaryService(AWishRecordMapper aWishRecordMapper, AEnrollScoreMapper aEnrollScoreMapper, AEnrollSpecialMapper aEnrollSpecialMapper, AMarjorPlanMapper aMarjorPlanMapper, AMarjorSubmitMapper aMarjorSubmitMapper, AEnrollUniversityMapper aEnrollUniversityMapper, BBusiWishUniversitiesMapper bBusiWishUniversitiesMapper, BBusiWishUniversitiesProfessionMapper bBusiWishUniversitiesProfessionMapper, ISysConfigService sysConfigService, EnrollRateCalculator enrollRateCalculator, BBusiWishRecordsMapper busiWishRecordsMapper) {
  61. this.aWishRecordMapper = aWishRecordMapper;
  62. this.aEnrollScoreMapper = aEnrollScoreMapper;
  63. this.aEnrollSpecialMapper = aEnrollSpecialMapper;
  64. this.aMarjorPlanMapper = aMarjorPlanMapper;
  65. this.aMarjorSubmitMapper = aMarjorSubmitMapper;
  66. this.aEnrollUniversityMapper = aEnrollUniversityMapper;
  67. this.bBusiWishUniversitiesMapper = bBusiWishUniversitiesMapper;
  68. this.bBusiWishUniversitiesProfessionMapper = bBusiWishUniversitiesProfessionMapper;
  69. this.sysConfigService = sysConfigService;
  70. this.enrollRateCalculator = enrollRateCalculator;
  71. this.busiWishRecordsMapper = busiWishRecordsMapper;
  72. // mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
  73. mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  74. }
  75. public Pair<List<UniversityDetailDTO.WishPlan>, List<UniversityDetailDTO.WishSubmit>> getUniversityHistory(Long universityId, String province, String examineType) {
  76. AMarjorPlan planCond = new AMarjorPlan();
  77. planCond.setUniversityId(universityId);
  78. planCond.setExamineeType(examineType);
  79. List<UniversityDetailDTO.WishPlan> planList = aMarjorPlanMapper.selectAMarjorPlanList(planCond).stream().map(t -> {
  80. UniversityDetailDTO.WishPlan p = new UniversityDetailDTO.WishPlan();
  81. p.setId(t.getId());
  82. p.setCollegeCode(t.getEnrollCode());
  83. p.setYear(String.valueOf(t.getYear()));
  84. p.setLevel(t.getLevel());
  85. p.setType("");
  86. p.setMarjorCode(t.getMajorCode());
  87. p.setMarjorName(t.getMajorName());
  88. p.setMarjorBelongs(t.getMajorEnrollCode());
  89. p.setMarjorDirection(t.getMajorDirection());
  90. p.setSpecialProject("");
  91. p.setPlanCount(t.getPlanTotal());
  92. p.setXuefei(String.valueOf(t.getXuefei()));
  93. p.setXuezhi(String.valueOf(t.getLengthOfSchooling()));
  94. p.setEnrollFormula(t.getEnrollFormula());
  95. p.setGroupsName(t.getMajorGroup());
  96. return p;
  97. }).collect(Collectors.toList());
  98. AMarjorSubmit submitCond = new AMarjorSubmit();
  99. submitCond.setUniversityId(universityId);
  100. submitCond.setEnrollType("初录");
  101. submitCond.setExamineeType(examineType);
  102. List<UniversityDetailDTO.WishSubmit> submitList = aMarjorSubmitMapper.selectAMarjorSubmitList(submitCond).stream().map(t -> {
  103. UniversityDetailDTO.WishSubmit s = new UniversityDetailDTO.WishSubmit();
  104. s.setId(t.getId());
  105. s.setYear(String.valueOf(t.getYear()));
  106. s.setLevel(t.getLevel());
  107. s.setType("");
  108. s.setMarjorCode("");
  109. s.setMarjorName(t.getMajorName());
  110. s.setMarjorBelongs("");
  111. s.setMarjorDirection(t.getMajorDirection());
  112. s.setSpecialProject("");
  113. s.setScore(null != t.getScore() ? t.getScore().longValue() : null);
  114. s.setSeat(null);
  115. s.setNumReal(null != t.getEnrollTotal() ? t.getEnrollTotal().longValue() : null);
  116. s.setEnrollFormula(t.getEnrollFormula());
  117. s.setGroupsName(t.getMajorGroup());
  118. return s;
  119. }).collect(Collectors.toList());
  120. return Pair.of(planList, submitList);
  121. }
  122. private void setOptionValue(VoluntaryDto.AIRenderRule r, String valueType, String valueRule, String options, Integer correctType, String correctValue, Integer scoreTotal) {
  123. r.setEnumInputType(VoluntaryDto.EnumInputType.valueOf(valueType));
  124. if (VoluntaryDto.EnumInputType.Score.equals(r.getEnumInputType())) {
  125. r.setOptions(new String[]{null == scoreTotal ? "0" : String.valueOf(scoreTotal.intValue())});
  126. } else if (StringUtils.isNotBlank(options) && (VoluntaryDto.EnumInputType.Checkbox.equals(r.getEnumInputType())
  127. || VoluntaryDto.EnumInputType.Radio.equals(r.getEnumInputType()) || VoluntaryDto.EnumInputType.Picker.equals(r.getEnumInputType()))) {
  128. r.setOptions(options.split(","));
  129. }
  130. // r.setRequired();
  131. // 词典选项类选项 // 词典类选项的优先级最高
  132. // String dictOptions;
  133. // 非词典选项 // 分制类规则,将多分制在此options中返回
  134. // String[] options;
  135. }
  136. public Integer getPlanYear(SysUser u) {
  137. return 2025;
  138. }
  139. public Integer getSubmitYear() {
  140. return 2024;
  141. }
  142. public R<List<VoluntaryDto.AIRenderRule>> getAIRenderRules(VoluntaryDto.AIRenderRequest req) {
  143. Map cond = new HashMap();
  144. cond.put("year", getPlanYear(SecurityUtils.getLoginUser().getUser()));
  145. Set<String> majorCodeSet = null;
  146. if (ArrayUtils.isNotEmpty(req.getMajorEnrollCodes()) && StringUtils.isNotBlank(req.getMajorEnrollCodes()[0])) {
  147. List<Long> majorEnrollCodes = Arrays.asList(req.getMajorEnrollCodes()).stream().map(t -> NumberUtils.toLong(t, 0L)).collect(Collectors.toList());
  148. majorCodeSet = aMarjorPlanMapper.selectMajorCodesByIds(majorEnrollCodes.toArray(new Long[majorEnrollCodes.size()])).stream().collect(Collectors.toSet());
  149. }
  150. if(CollectionUtils.isNotEmpty(majorCodeSet)) {
  151. cond.put("majorCodes", majorCodeSet.toArray(new String[majorCodeSet.size()]));
  152. } else if (ArrayUtils.isNotEmpty(req.getMajorCodes()) && StringUtils.isNotBlank(req.getMajorCodes()[0])) {
  153. cond.put("majorCodes", req.getMajorCodes());
  154. } else if (ArrayUtils.isNotEmpty(req.getMajorTypes()) && StringUtils.isNotBlank(req.getMajorTypes()[0])) {
  155. cond.put("majorTypes", req.getMajorTypes());
  156. } else if (StringUtils.isNotBlank(req.getMajorCategory())) {
  157. cond.put("majorCategory", req.getMajorCategory());
  158. }
  159. cond.put("universityCode", req.getUniversityCode());
  160. return R.ok(findMatchRules(cond, req.getRenderType() == 1, req.getRenderType() == 2));
  161. }
  162. public R<VoluntaryDto.SingleResponse> postSingleResult(VoluntaryDto.SingleRequest req, Boolean isScoreOnly) {
  163. VoluntaryDto.MultipleRequest mr = new VoluntaryDto.MultipleRequest();
  164. VoluntaryDto.CollegeMajorDto collegeMajor = new VoluntaryDto.CollegeMajorDto();
  165. collegeMajor.setCode(req.getUniversityCode());
  166. if (StringUtils.isNotBlank(req.getMajorCode())) {
  167. collegeMajor.setMajorCodes(Lists.newArrayList(req.getMajorCode()));
  168. }
  169. if (StringUtils.isNotBlank(req.getMajorEnrollCode())) {
  170. collegeMajor.setMajorEnrollCodes(Lists.newArrayList(req.getMajorEnrollCode()));
  171. }
  172. collegeMajor.setForm(req.getForm());
  173. mr.setUniversities(Lists.newArrayList(collegeMajor));
  174. List<VoluntaryDto.SingleResponse> respList = postMultipleResult(mr, isScoreOnly).getData();
  175. if (respList.size() > 0) {
  176. Optional<VoluntaryDto.SingleResponse> optionalSingleResponse = respList.stream().filter(t -> StringUtils.isBlank(t.getMajorDirection())).findFirst();
  177. if (optionalSingleResponse.isPresent()) {
  178. return R.ok(optionalSingleResponse.get());
  179. }
  180. }
  181. return respList.size() > 0 ? R.ok(respList.get(0)) : null;
  182. }
  183. public R<List<VoluntaryDto.SingleResponse>> postMultipleResult(VoluntaryDto.MultipleRequest req, boolean isScoreOnly) {
  184. List<VoluntaryDto.SingleResponse> respList = Lists.newArrayList();
  185. for (VoluntaryDto.CollegeMajorDto cm : req.getUniversities()) {
  186. Map cond = new HashMap();
  187. if (CollectionUtils.isNotEmpty(cm.getMajorEnrollCodes()) && StringUtils.isNotBlank(cm.getMajorEnrollCodes().get(0))) {
  188. cond.put("majorEnrollCodes", cm.getMajorEnrollCodes()); // TODO 代替majorCodes更精准,只是现在没有,使用planId代替
  189. } else if (CollectionUtils.isNotEmpty(cm.getMajorCodes()) && StringUtils.isNotBlank(cm.getMajorCodes().get(0))) {
  190. cond.put("majorCodes", cm.getMajorCodes());
  191. } else if (StringUtils.isNotBlank(req.getMajorCategory())) {
  192. cond.put("majorCategory", req.getMajorCategory());
  193. }
  194. cond.put("universityCode", StringUtils.trimToEmpty(cm.getCode()));
  195. List<VoluntaryDto.AIResponse> responses = findMajorSuggest(getPlanYear(SecurityUtils.getLoginUser().getUser()), getSubmitYear(), cond, cm.getForm(), false, isScoreOnly);
  196. if(CollectionUtils.isNotEmpty(responses)) {
  197. responses.stream().forEach(t -> respList.addAll(t.getMajorDetails()));
  198. }
  199. }
  200. return R.ok(respList);
  201. }
  202. public TableDataInfo postAIResult(VoluntaryDto.AIRequest req) {
  203. VoluntaryDto.AIRequestFilter filter = req.getFilter();
  204. Map cond = new HashMap();
  205. if (CollectionUtils.isNotEmpty(filter.getMajorTypes()) && StringUtils.isNotBlank(filter.getMajorTypes().get(0))) {
  206. cond.put("majorTypes", filter.getMajorTypes());
  207. } else if (StringUtils.isNotBlank(req.getMajorCategory())) {
  208. cond.put("majorCategory", req.getMajorCategory());
  209. }
  210. cond.put("universityName", filter.getKeyword());// 院校名称
  211. if (CollectionUtils.isNotEmpty(filter.getLevel())) {
  212. cond.put("universityLevel", filter.getLevel());// 办学层次
  213. }
  214. if (CollectionUtils.isNotEmpty(filter.getType())) {
  215. cond.put("universityType", filter.getType());// 院校类型
  216. }
  217. if (CollectionUtils.isNotEmpty(filter.getNatureTypeCN())) {
  218. cond.put("universityNatureType", filter.getNatureTypeCN()); // 办学类型
  219. }
  220. if (CollectionUtils.isNotEmpty(filter.getLocation())) {
  221. cond.put("universityLocation", filter.getLocation()); // 院校省份
  222. }
  223. if (null != filter.getHasClearing()) {
  224. cond.put("hasClearing", filter.getHasClearing() ? "补录" : "初录");
  225. }
  226. List<VoluntaryDto.AIResponse> responses = findMajorSuggest(getPlanYear(SecurityUtils.getLoginUser().getUser()), getSubmitYear(), cond, req.getForm(), true, false);
  227. if (CollectionUtils.isEmpty(responses)) {
  228. return new TableDataInfo(Lists.newArrayList(), 0);
  229. }
  230. if (null != filter.getEnumPickEmpty()) {
  231. responses = responses.stream().filter(t -> filter.getEnumPickEmpty().equals(t.getEnumPickEmpty())).collect(Collectors.toList());
  232. } else if (null != filter.getEnumPickType() && !VoluntaryDto.EnumPickType.All.equals(filter.getEnumPickType())) {
  233. responses = responses.stream().filter(t -> filter.getEnumPickType().equals(t.getEnumPickType())).collect(Collectors.toList());
  234. }
  235. Integer start = null != req.getPageNum() ? (req.getPageNum() - 1) * req.getPageSize() : 0;
  236. List<VoluntaryDto.AIResponse> finalList = responses.subList(start, Math.min(start + (null == req.getPageSize() ? 10 : req.getPageSize()), responses.size()));
  237. TableDataInfo tableDataInfo = new TableDataInfo(finalList, responses.size());
  238. tableDataInfo.setCode(200);
  239. return tableDataInfo;
  240. }
  241. private List<VoluntaryDto.AIRenderRule> findMatchRules(Map cond, boolean isAi, boolean isScore) {
  242. String examType = SecurityUtils.getLoginUser().getUser().getExamType().title(); // TODO MF
  243. String gender = "0".equals(SecurityUtils.getLoginUser().getUser().getSex()) ? "男生" : "女生";
  244. cond.put("examineeType", examType);
  245. List<AEnrollScore> enrollScoreList = aEnrollScoreMapper.selectListByRuleCond(cond);
  246. List<VoluntaryDto.AIRenderRule> ruleList = Lists.newArrayList();
  247. Set<String> existItemSet = Sets.newHashSet();
  248. String vt;
  249. VoluntaryDto.AIRenderRule scoreRateRule = null;
  250. for (AEnrollScore s : enrollScoreList) { // 跳过统计类和提示类的规则
  251. if (null == (vt = s.getValueType()) || vt.startsWith("Stat") || vt.equals("Notice")) {
  252. continue;
  253. } else if(isScore && !"学考".equals(s.getItemCategory()) && !"校考".equals(s.getItemCategory())) {
  254. continue;
  255. } else if (vt.equals("ScoreRate")) {
  256. if (!isAi) {
  257. vt = "Score";
  258. } else if (existItemSet.add("ScoreRate")) {
  259. scoreRateRule = new VoluntaryDto.AIRenderRule();
  260. scoreRateRule.setEnumRuleCategory(VoluntaryDto.EnumRuleCategory.Enroll);
  261. scoreRateRule.setFieldName("ScoreRate");
  262. scoreRateRule.setEnumInputType(VoluntaryDto.EnumInputType.Number);
  263. scoreRateRule.setMin(0);
  264. scoreRateRule.setMax(100);
  265. scoreRateRule.setLabel("技能测试得分率");
  266. scoreRateRule.setDescription("%");
  267. continue;
  268. } else {
  269. continue;
  270. }
  271. }
  272. if (!existItemSet.add(s.getItemField())) {
  273. continue;
  274. }
  275. VoluntaryDto.AIRenderRule r = new VoluntaryDto.AIRenderRule();
  276. r.setEnumRuleCategory(VoluntaryDto.EnumRuleCategory.Enroll);
  277. r.setFieldName(s.getItemField());
  278. setOptionValue(r, vt, s.getValueRule(), s.getValueOptional(), s.getCorrectType(), s.getCorrectValue(), s.getScoreTotal());
  279. r.setRegex(s.getRegex());
  280. r.setLabel(s.getItemName());
  281. r.setDescription(s.getDescription());
  282. r.setPlaceholder(s.getPlaceholder());
  283. r.setTips(s.getTips());
  284. r.setDefaultValue(s.getDefaultValue());
  285. r.setDotDisable(null != s.getDotDisable() && s.getDotDisable() > 0);
  286. r.setKeyboardMode(s.getKeyboardMode()); // number card car,默认number
  287. ruleList.add(r);
  288. }
  289. if (null != scoreRateRule) {
  290. ruleList.add(scoreRateRule);
  291. }
  292. if(isScore) {
  293. return ruleList;
  294. }
  295. List<AEnrollSpecial> enrollSpecialList = Lists.newArrayList();
  296. Map<String, AEnrollSpecial> mergeEnrollSpecialMap = Maps.newHashMap();
  297. List<AEnrollSpecial> oriErollSpecialList = aEnrollSpecialMapper.selectListByRuleCond(cond);
  298. for (AEnrollSpecial s : oriErollSpecialList) {
  299. if (VoluntaryDto.EnumRuleType.Readonly.name().equals(s.getItemType()) || StringUtils.isNotBlank(s.getGender()) && !s.getGender().equals(gender)) {
  300. continue;
  301. }
  302. if (null == s.getItemGroup() || !"Checkbox".equals(s.getValueType())) {
  303. enrollSpecialList.add(s);
  304. continue;
  305. }
  306. //
  307. String key = s.getItemGroup(); // s.getUniversityId() + "_" + s.getMajorName() + "_" + StringUtils.trimToEmpty(s.getMajorDirection()) + "_" + StringUtils.trimToEmpty(s.getGender()) + "_" + s.getItemCategory() + "_" +
  308. AEnrollSpecial exist = mergeEnrollSpecialMap.get(key);
  309. if (null == exist) {
  310. // 修正分组后的名称及字段名
  311. String[] groupNameField = StringUtils.split(s.getItemGroup(), "_");
  312. s.setValueOptional(s.getItemName());
  313. s.setItemName(groupNameField[0]);
  314. s.setItemField(groupNameField[groupNameField.length > 1 ? 1 : 0]);
  315. mergeEnrollSpecialMap.put(key, s);
  316. enrollSpecialList.add(s);
  317. continue;
  318. }
  319. if(StringUtils.isBlank(exist.getValueOptional()) || !exist.getValueOptional().contains(s.getItemName())) {
  320. exist.setValueOptional(exist.getValueOptional() + "," + s.getItemName());
  321. }
  322. }
  323. Map<String,String> mutexOptionMap = getMutexOptionMap();
  324. for (AEnrollSpecial s : enrollSpecialList) {
  325. if (null == (vt = s.getValueType()) || vt.startsWith("Stat") || vt.equals("Notice") || !existItemSet.add(s.getItemField())) {
  326. continue;
  327. }
  328. VoluntaryDto.AIRenderRule r = new VoluntaryDto.AIRenderRule();
  329. r.setEnumRuleCategory(VoluntaryDto.EnumRuleCategory.Special);
  330. r.setFieldName(s.getItemField());
  331. setOptionValue(r, s.getValueType(), s.getValueRule(), s.getValueOptional(), s.getCorrectType(), s.getCorrectValue(), null);
  332. if (VoluntaryDto.EnumInputType.Checkbox.equals(r.getEnumInputType())) {
  333. r.setMutexOption(mutexOptionMap.get(r.getFieldName()));
  334. }
  335. r.setRegex(s.getRegex());
  336. r.setLabel(s.getItemName());
  337. r.setDescription(s.getDescription());
  338. r.setPlaceholder(s.getPlaceholder());
  339. r.setTips(s.getTips());
  340. r.setDefaultValue(s.getDefaultValue());
  341. r.setDotDisable(null != s.getDotDisable() && s.getDotDisable() > 0);
  342. r.setKeyboardMode(s.getKeyboardMode()); // number card car,默认number
  343. ruleList.add(r);
  344. }
  345. return ruleList;
  346. }
  347. private Map<String, String> getMutexOptionMap() {
  348. String mutexOptionConf = sysConfigService.selectConfigByKey("voluntary.rule.mutexOption");
  349. Map<String, String> mutexOptionMap = Maps.newHashMap();
  350. if (StringUtils.isNotBlank(mutexOptionConf)) {
  351. try {
  352. JSONArray.parseArray(mutexOptionConf).forEach(l -> {
  353. JSONObject lo = (JSONObject) l;
  354. mutexOptionMap.put(lo.getString("n"), lo.getString("m"));
  355. });
  356. } catch (JSONException e) {
  357. }
  358. }
  359. return mutexOptionMap;
  360. }
  361. // TODO 同一专业号有多个专业方向
  362. private List<VoluntaryDto.AIResponse> findMajorSuggest(Integer planYear, Integer submitYear, Map cond, Map<String, String> paramMap, boolean isAi, boolean isScoreOnly) {
  363. String examType = SecurityUtils.getLoginUser().getUser().getExamType().title(); // TODO MF
  364. String gender = "0".equals(SecurityUtils.getLoginUser().getUser().getSex()) ? "男生" : "女生";
  365. cond.remove("year");
  366. cond.put("examineeType", examType);
  367. Map<Long, List<AMarjorPlan>> currUniversityMajorPlansMap = Maps.newHashMap();
  368. Map<Long, List<AMarjorPlan>> historyUniversityMajorPlansMap = Maps.newHashMap();
  369. List<Map<Long, List<AMarjorPlan>>> bothPlanList = Lists.newArrayList(currUniversityMajorPlansMap, historyUniversityMajorPlansMap);
  370. Set<String> existSet = Sets.newHashSet();
  371. for (AMarjorPlan mp : aMarjorPlanMapper.selectListByRuleCond(cond)) {
  372. String key = mp.getUniversityId() + mp.getMajorName() + StringUtils.trimToEmpty(mp.getMajorDirection()) + mp.getYear(); // TODO 考虑为什么会重的问题 examType ?
  373. if (!existSet.add(key)) {
  374. continue;
  375. }
  376. for(Map<Long, List<AMarjorPlan>> tmpMap : bothPlanList) {
  377. if (!mp.getYear().equals(planYear) && tmpMap == currUniversityMajorPlansMap) {
  378. continue;
  379. }
  380. List<AMarjorPlan> tmpMpList = tmpMap.get(mp.getUniversityId());
  381. if (null == tmpMpList) {
  382. tmpMpList = Lists.newArrayList(mp);
  383. tmpMap.put(mp.getUniversityId(), tmpMpList);
  384. } else {
  385. tmpMpList.add(mp);
  386. }
  387. }
  388. }
  389. Map<Long, List<BBusiWishUniversitiesProfession>> universityMajorsMap = Maps.newHashMap();
  390. // 有计划以计划为准的输出,无计划时则以所有专业输出为准
  391. Map<Long, BBusiWishUniversities> universitiesMap = null;
  392. if (MapUtils.isEmpty(currUniversityMajorPlansMap)) {
  393. Map<String, List<BBusiWishUniversitiesProfession>> tmpUniversityMajorsMap = bBusiWishUniversitiesProfessionMapper.selectListByRuleCond(cond).stream().collect(Collectors.groupingBy(BBusiWishUniversitiesProfession::getCollegeCode));
  394. if (MapUtils.isNotEmpty(tmpUniversityMajorsMap)) {
  395. Map uCond = Maps.newHashMap();
  396. uCond.put("codes", tmpUniversityMajorsMap.keySet());
  397. universitiesMap = Maps.newHashMap();
  398. for (BBusiWishUniversities u : bBusiWishUniversitiesMapper.selectBBusiWishUniversitiesListSimpleByMap(uCond)) {
  399. universitiesMap.put(u.getId(), u);
  400. universityMajorsMap.put(u.getId(), tmpUniversityMajorsMap.get(u.getCode()));
  401. }
  402. }
  403. } else {
  404. cond.put("universityIds", currUniversityMajorPlansMap.keySet());
  405. cond.put("examType", Constant.EXAM_TYPE_PG);
  406. Map<String, List<BBusiWishUniversitiesProfession>> tmpUniversityMajorsMap = bBusiWishUniversitiesProfessionMapper.selectListByRuleCond(cond).stream().collect(Collectors.groupingBy(BBusiWishUniversitiesProfession::getCollegeCode));
  407. cond.remove("universityIds");
  408. if (MapUtils.isNotEmpty(tmpUniversityMajorsMap)) {
  409. Map uCond = new HashMap();
  410. uCond.put("ids", currUniversityMajorPlansMap.keySet());
  411. universitiesMap = Maps.newHashMap();
  412. for (BBusiWishUniversities u : bBusiWishUniversitiesMapper.selectBBusiWishUniversitiesListSimpleByIds(uCond)) {
  413. universitiesMap.put(u.getId(), u);
  414. universityMajorsMap.put(u.getId(), tmpUniversityMajorsMap.get(u.getCode()));
  415. }
  416. }
  417. }
  418. // 无计划,也无院校专业
  419. if(MapUtils.isEmpty(universitiesMap)) {
  420. return Lists.newArrayList();
  421. }
  422. // 查询涉及的规则
  423. cond.put("year", planYear);
  424. cond.put("universityIds", universitiesMap.keySet());
  425. Map<Long, List<AEnrollScore>> universityScoreListMap = aEnrollScoreMapper.selectListByRuleCond(cond).stream().collect(Collectors.groupingBy(AEnrollScore::getUniversityId));
  426. Map<Long, List<AEnrollSpecial>> universitySpecialListMap = aEnrollSpecialMapper.selectListByRuleCond(cond).stream().collect(Collectors.groupingBy(AEnrollSpecial::getUniversityId));
  427. cond.remove("universityIds");
  428. cond.remove("year");
  429. Map<String, String> mutexOptionMap = getMutexOptionMap();
  430. List<VoluntaryDto.AIResponse> aiRespList = Lists.newArrayList();
  431. for (Long universityId : universitiesMap.keySet()) {
  432. List<AEnrollScore> scoreList = universityScoreListMap.get(universityId); // 分数条件
  433. List<AEnrollSpecial> specialList = universitySpecialListMap.get(universityId);// 专项条件
  434. List<AMarjorPlan> currPlanList = currUniversityMajorPlansMap.get(universityId);// 当年计划
  435. List<AMarjorPlan> historyPlanList = historyUniversityMajorPlansMap.get(universityId);// 历年计划
  436. BBusiWishUniversities u = universitiesMap.get(universityId);// 院校
  437. List<BBusiWishUniversitiesProfession> professionList = universityMajorsMap.get(u.getId()); // 所有专业
  438. // 院校变更标志
  439. AEnrollUniversity euCond = new AEnrollUniversity();
  440. euCond.setUniversityId(u.getId());
  441. euCond.setYear(planYear);
  442. List<AEnrollUniversity> enrollUniversityList = aEnrollUniversityMapper.selectAEnrollUniversityList(euCond);
  443. // 院校历史情况
  444. AMarjorSubmit submitCond = new AMarjorSubmit();
  445. submitCond.setExamineeType(examType);
  446. submitCond.setUniversityId(universityId);
  447. List<AMarjorSubmit> submitList = aMarjorSubmitMapper.selectAMarjorSubmitList(submitCond);
  448. VoluntaryDto.AIResponse aiResp = new VoluntaryDto.AIResponse();
  449. aiResp.setUniversity(u);
  450. VoluntaryDto.SingleResponse sr;
  451. List<VoluntaryDto.SingleResponse> singleResponseList = Lists.newArrayList();
  452. if (CollectionUtils.isNotEmpty(currPlanList)) {
  453. aiResp.setEnrollCode(currPlanList.get(0).getEnrollCode());
  454. Map<String, BBusiWishUniversitiesProfession> professionMap = null == professionList ? Maps.newHashMap() : professionList.stream().collect(Collectors.toMap(BBusiWishUniversitiesProfession::getName, Function.identity()));
  455. for (AMarjorPlan mp : currPlanList) {
  456. if (null != (sr = buildSingleResponse(submitYear, planYear, gender, mp, professionMap.get(mp.getMajorName()), enrollUniversityList, paramMap, u,
  457. historyPlanList, submitList, scoreList, specialList, mutexOptionMap, isAi, isScoreOnly))) {
  458. singleResponseList.add(sr);
  459. }
  460. }
  461. } else {
  462. for (BBusiWishUniversitiesProfession prof : professionList) {
  463. if (null != (sr = buildSingleResponse(submitYear, planYear, gender, null, prof, enrollUniversityList, paramMap, u,
  464. historyPlanList, submitList, scoreList, specialList, mutexOptionMap, isAi, isScoreOnly))) {
  465. singleResponseList.add(sr);
  466. }
  467. }
  468. }
  469. aiResp.setMajorDetails(singleResponseList);
  470. sortAndExtractEnroll(singleResponseList, aiResp);
  471. aiRespList.add(aiResp);
  472. }
  473. Collections.sort(aiRespList, new Comparator<VoluntaryDto.AIResponse>() {
  474. @Override
  475. public int compare(VoluntaryDto.AIResponse o1, VoluntaryDto.AIResponse o2) {
  476. Integer d1 = o1.getEnrollRate();
  477. Integer d2 = o2.getEnrollRate();
  478. d1 = null == d1 ? 999 : (d1 == 0 ? 998 : d1);
  479. d2 = null == d2 ? 999 : (d2 == 0 ? 998 : d2);
  480. return d1.compareTo(d2);
  481. }
  482. });
  483. return aiRespList;
  484. }
  485. private VoluntaryDto.SingleResponse buildSingleResponse(Integer submitYear, Integer planYear, String gender, AMarjorPlan currPlan, BBusiWishUniversitiesProfession prof,
  486. List<AEnrollUniversity> enrollUniversityList, Map<String, String> paramMap, BBusiWishUniversities u,
  487. List<AMarjorPlan> historyPlanList, List<AMarjorSubmit> submitList,
  488. List<AEnrollScore> scoreList, List<AEnrollSpecial> specialList, Map<String,String> mutexOptionMap, boolean isAi, boolean isScoreOnly) {
  489. if (null != submitList) {
  490. Collections.sort(submitList, new Comparator<AMarjorSubmit>() {
  491. @Override
  492. public int compare(AMarjorSubmit o1, AMarjorSubmit o2) {
  493. int iRet;
  494. if (0 != (iRet = o1.getYear().compareTo(o2.getYear()))) {
  495. return -iRet;
  496. }
  497. if (0 != (iRet = o1.getMajorName().compareTo(o2.getMajorName()))) {
  498. return -iRet;
  499. }
  500. if (null == o1.getMajorDirection()) {
  501. if (null != o2.getMajorDirection()) {
  502. return 1;
  503. }
  504. return 0;
  505. } else if (null == o2.getMajorDirection()) {
  506. return -1;
  507. }
  508. return 0;
  509. }
  510. });
  511. } else {
  512. submitList = Lists.newArrayList();
  513. }
  514. if (null != historyPlanList) {
  515. Collections.sort(historyPlanList, new Comparator<AMarjorPlan>() {
  516. @Override
  517. public int compare(AMarjorPlan o1, AMarjorPlan o2) {
  518. int iRet;
  519. if (0 != (iRet = o1.getYear().compareTo(o2.getYear()))) {
  520. return -iRet;
  521. }
  522. if (0 != (iRet = o1.getMajorName().compareTo(o2.getMajorName()))) {
  523. return -iRet;
  524. }
  525. if (null == o1.getMajorDirection()) {
  526. if (null != o2.getMajorDirection()) {
  527. return 1;
  528. }
  529. return 0;
  530. } else if (null == o2.getMajorDirection()) {
  531. return -1;
  532. }
  533. return o1.getMajorDirection().compareTo(o2.getMajorDirection());
  534. }
  535. });
  536. } else {
  537. historyPlanList = Lists.newArrayList();
  538. }
  539. // 录取线
  540. List<VoluntaryDto.MajorClearingHistory> clearings = Lists.newArrayList(); // 补录情况
  541. List<VoluntaryDto.MajorEnrollHistory> histories = Lists.newArrayList(); // 初录情况
  542. List<VoluntaryDto.MajorEnrollRule> ruleMatchList = Lists.newArrayList();
  543. List<VoluntaryDto.MajorEnrollRule> improveList = Lists.newArrayList();
  544. Double tmpValue; // 总分,得分
  545. AMarjorSubmit lastSubmit = null;
  546. Boolean typeChange = false;
  547. VoluntaryDto.FormulaScoreStat formulaScoreStat = new VoluntaryDto.FormulaScoreStat();
  548. String needMajor = StringUtils.trimToEmpty(null != currPlan ? currPlan.getMajorName() : (null != prof ? prof.getName() : ""));
  549. String needDirect = StringUtils.trimToEmpty(null != currPlan ? currPlan.getMajorDirection() : "");
  550. String needMajorDirect = needMajor + needDirect;
  551. // 判断是否政策变化情况
  552. List<AEnrollUniversity> validEuList = enrollUniversityList.stream().filter(t -> t.getMajorNames().contains(needMajor)).collect(Collectors.toList());
  553. typeChange = CollectionUtils.isNotEmpty(validEuList) && new Integer(1).equals(validEuList.get(0).getTypeChange());
  554. Set<String> existSet = Sets.newHashSet();
  555. existSet.clear();
  556. List<AMarjorSubmit> validSubmitList = Lists.newArrayList();
  557. if (null != submitList) {
  558. for (AMarjorSubmit s : submitList) { // 历年录取通过专业分来判断差别
  559. String majorNameDirect = s.getMajorName() + StringUtils.trimToEmpty(s.getMajorDirection());
  560. if (needMajorDirect.equals(majorNameDirect) && existSet.add(needMajorDirect + s.getYear() + s.getEnrollType())) {
  561. validSubmitList.add(s);
  562. if (s.getYear().equals(submitYear) && s.getEnrollType().equals("初录") ) {
  563. lastSubmit = s; // TODO 初录,补录与分数线的关系 要处理
  564. }
  565. }
  566. }
  567. }
  568. // 分项规则检查
  569. existSet.clear();
  570. Double currTotal = null;
  571. if (null != scoreList) {
  572. Double inputScoreRate = NumberUtils.toInt(paramMap.get("ScoreRate"), 0) / 100.0;
  573. List<AEnrollScore> fEnrollScoreList = Lists.newArrayList();
  574. String inclMajorDirection = needMajor + "(" + needDirect + ")";
  575. String exclMajorDirection = needMajor + "(";
  576. Map<Boolean, List<AEnrollScore>> majorEnrollScoresMap = scoreList.stream().collect(Collectors.groupingBy(t -> StringUtils.isNotBlank(t.getMajorNames())));
  577. List<AEnrollScore> tmpEnrollScoreList = majorEnrollScoresMap.get(Boolean.TRUE);
  578. AEnrollScore skillEnrollScore = null;
  579. if (null != tmpEnrollScoreList) {
  580. for (AEnrollScore r : tmpEnrollScoreList) {
  581. if (StringUtils.isNotBlank(r.getMajorNames())) {
  582. if (!r.getMajorNames().contains(needMajor)) {
  583. continue;
  584. }
  585. if (StringUtils.isNotBlank(r.getMajorDirections())) { // 有单独的方向时,分开判断
  586. if (StringUtils.isBlank(needDirect) || !r.getMajorDirections().contains(needDirect)) {
  587. continue;
  588. }
  589. } else if (StringUtils.isNotBlank(needDirect)) { // 有direct要求时,这里需要有 "<专业>(<方向>)
  590. if (!r.getMajorNames().contains(inclMajorDirection)) {
  591. continue;
  592. }
  593. } else if (r.getMajorNames().contains(exclMajorDirection)) { // 当无direct要求时,这时不能有 "<专业>("
  594. continue;
  595. }
  596. }
  597. Boolean isSkillScore = isScoreOnly && r.getItemType().equals("ScoreSkill");
  598. if(isSkillScore) {
  599. skillEnrollScore = r;
  600. }
  601. appendScoreRule(formulaScoreStat, existSet, r, fEnrollScoreList, isAi, paramMap, inputScoreRate, isSkillScore);
  602. }
  603. }
  604. if (fEnrollScoreList.size() == 0 && null != (tmpEnrollScoreList = majorEnrollScoresMap.get(Boolean.FALSE))) {
  605. for (AEnrollScore r : tmpEnrollScoreList) {
  606. Boolean isSkillScore = isScoreOnly && r.getItemType().equals("ScoreSkill");
  607. if(isSkillScore) {
  608. skillEnrollScore = r;
  609. }
  610. appendScoreRule(formulaScoreStat, existSet, r, fEnrollScoreList, isAi, paramMap, inputScoreRate, isSkillScore);
  611. }
  612. }
  613. boolean isSameYear = null == lastSubmit || planYear.equals(lastSubmit.getYear());
  614. currTotal = formulaScoreStat.getTypeValue("StatRateScore", "", false);
  615. for (AEnrollScore r : fEnrollScoreList) {
  616. VoluntaryDto.MajorEnrollRule mr = buildEnrollRule(r);
  617. mr.setContent(StringUtils.isNotBlank(r.getEqualFormula()) ? r.getEqualFormula() : r.getEnrollFormula());
  618. if(isScoreOnly) {
  619. String vt;
  620. if (null != (vt = r.getValueType()) && vt.startsWith("Score")) {
  621. improveList.add(mr);
  622. mr.setCategory(VoluntaryDto.EnumRuleCategory.Enroll);
  623. mr.setType(VoluntaryDto.EnumRuleType.ScoreTotal);
  624. // mr.setValue(null != currTotal ? String.valueOf(currTotal.intValue()) : "");
  625. if (null == lastSubmit || null == lastSubmit.getScore() || null == lastSubmit.getScoreTotal() && !isSameYear) {
  626. mr.setFailedMessage("缺历史录取数据");
  627. continue;
  628. }
  629. mr.setYear(lastSubmit.getYear());
  630. if (typeChange) {
  631. mr.setFailedMessage("规则变更,无法计算");
  632. continue;
  633. } else if (!formulaScoreStat.isScoreValid() && (!isAi || inputScoreRate <= 0.0)) {
  634. mr.setFailedMessage("用户没有填写完整");
  635. continue;
  636. }
  637. Integer validScoreTotal = isSameYear ? formulaScoreStat.getAllTotal() : lastSubmit.getScoreTotal();
  638. Double currScoreRate = isSameYear ? 1.0 : formulaScoreStat.getAllTotal() * 1.0 / lastSubmit.getScoreTotal(); // 去年的分转成今年的分
  639. EnrollRateCalculator.RateLevel rl;
  640. if (null != lastSubmit.getScore() && currTotal != null && null != (rl = enrollRateCalculator.satisfy(validScoreTotal, lastSubmit.getScore(), currTotal / currScoreRate))) { // 按去年标准算概率
  641. Double skillRate = Double.parseDouble(skillEnrollScore.getValueRule());
  642. mr.setValue(String.valueOf(Math.round(Math.round(lastSubmit.getScore() * currScoreRate) - currTotal) / skillRate));
  643. mr.setValid(true);
  644. } else {
  645. mr.setFailedMessage("没有对应计算条件");
  646. continue;
  647. }
  648. } else {
  649. ruleMatchList.add(mr);
  650. mr.setValid(true);
  651. }
  652. continue;
  653. }
  654. ruleMatchList.add(mr);
  655. String vt;
  656. if (null == (vt = r.getValueType())) { // 无值时相当于直接通过
  657. mr.setValid(true);
  658. } else if (null != (vt = r.getValueType()) && vt.startsWith("Score")) {
  659. mr.setValue(null != currTotal ? String.valueOf(currTotal.intValue()) : "");
  660. if (null == lastSubmit || null == lastSubmit.getScore() || null == lastSubmit.getScoreTotal() && !isSameYear) {
  661. mr.setFailedMessage("缺历史录取数据");
  662. continue;
  663. } else if (typeChange) {
  664. mr.setFailedMessage("规则变更,无法计算");
  665. continue;
  666. } else if (!formulaScoreStat.isValid() && (!isAi || inputScoreRate <= 0.0)) {
  667. mr.setFailedMessage("用户没有填写完整");
  668. continue;
  669. }
  670. mr.setValid(true);
  671. Integer validScoreTotal = isSameYear ? formulaScoreStat.getAllTotal() : lastSubmit.getScoreTotal();
  672. Double currScoreRate = isSameYear ? 1.0 : formulaScoreStat.getAllTotal() * 1.0 / lastSubmit.getScoreTotal(); // 去年的分转成今年的分
  673. Double currScore = null;
  674. if (null != lastSubmit.getCulturalScore()) {
  675. if (null == (currScore = formulaScoreStat.getTypeValue("ScoreSingle", "", false))) {
  676. currScore = formulaScoreStat.getTypeValue("ScoreBase", "", false);
  677. }
  678. if (null != currScore && currScore < lastSubmit.getCulturalScore() * currScoreRate) {
  679. // mr.setValid(false);
  680. mr.setMissingValue(String.valueOf(Math.round(lastSubmit.getCulturalScore() * currScoreRate - currScore)));
  681. mr.setFailedMessage("校考分低于分数线");
  682. }
  683. }
  684. if (null != lastSubmit.getProfScore() && null != (currScore = formulaScoreStat.getTypeValue("ScoreSkill", "", false)) && currScore < lastSubmit.getProfScore() * currScoreRate) {
  685. // mr.setValid(false);
  686. mr.setMissingValue(String.valueOf(Math.round(lastSubmit.getProfScore() * currScoreRate - currScore)));
  687. mr.setFailedMessage("技能测试分低于分数线");
  688. }
  689. // 检查总分,学考(文化),专业(技能)
  690. EnrollRateCalculator.RateLevel rl;
  691. if (null != lastSubmit.getScore() && currTotal != null && null != (rl = enrollRateCalculator.calSchoolEnrollRate(validScoreTotal, lastSubmit.getScore(), currTotal / currScoreRate))) { // 按去年标准算概率
  692. mr.setRl(rl);
  693. mr.setEnrollRate(rl.rate);
  694. mr.setEnrollRateText(rl.typeLabel);
  695. mr.setEnumPickType(rl.type);
  696. if (mr.getValid() && currTotal < lastSubmit.getScore() * currScoreRate) {
  697. // mr.setValid(false);
  698. mr.setMissingValue(String.valueOf(Math.round(lastSubmit.getScore() * currScoreRate - currTotal))); // 按今年的算差值
  699. mr.setFailedMessage("综合分低于分数线");
  700. }
  701. }
  702. } else { // 专项符合型
  703. mr.setValid(processPassCheck(paramMap, formulaScoreStat, r.getValueRule(), mr, r.getValueType(), r.getItemField(), r.getValuePassRule()));
  704. }
  705. }
  706. }
  707. if (null != specialList) {
  708. existSet.clear();
  709. List<AEnrollSpecial> validSpecialList = Lists.newArrayList();
  710. Map<String, Pair<String, Set<String>>> groupMutexOptionsMap = Maps.newHashMap();
  711. for (AEnrollSpecial r : specialList) {
  712. if (StringUtils.isNotBlank(r.getGender()) && !r.getGender().equals(gender)
  713. || StringUtils.isNotBlank(r.getMajorName()) && !Sets.newHashSet(r.getMajorName().split(",")).contains(needMajor)
  714. || StringUtils.isNotBlank(r.getMajorDirection()) && !r.getMajorDirection().equals(needDirect)) {
  715. continue;
  716. }
  717. if(r.getValueType().startsWith("StatBMI")) { // TODO 身高体重的名称需要是固定的
  718. String height = paramMap.get("身高");
  719. String weight = paramMap.get("体重");
  720. Integer iHeight;
  721. if (StringUtils.isNotBlank(height) && StringUtils.isNotBlank(weight) && (iHeight = NumberUtils.toInt(height, 0)) > 0) {
  722. paramMap.put(r.getItemField(), String.valueOf(NumberUtils.toInt(height, 0) * 100 / iHeight));
  723. }
  724. } else if (null != r.getItemGroup() && "Checkbox".equals(r.getValueType())) {
  725. Pair<String, Set<String>> p = getGroupOptions(groupMutexOptionsMap, mutexOptionMap, paramMap, r.getItemGroup());
  726. if (p.getRight().size() == 0) { // 无值时
  727. paramMap.put(r.getItemField(), null);
  728. } else if (StringUtils.isNotBlank(p.getLeft()) && p.getRight().contains(p.getLeft())) { // 选中互斥时全是否
  729. paramMap.put(r.getItemField(), !p.getLeft().contains("无") ? "[\"是\"]" : "[\"否\"]");
  730. } else {
  731. paramMap.put(r.getItemField(), p.getRight().contains(r.getItemName()) ? "[\"是\"]" : "[\"否\"]");
  732. }
  733. }
  734. validSpecialList.add(r);
  735. }
  736. for (AEnrollSpecial r : validSpecialList) {
  737. VoluntaryDto.MajorEnrollRule mr = new VoluntaryDto.MajorEnrollRule();
  738. mr.setCategory(VoluntaryDto.EnumRuleCategory.Special);
  739. mr.setContent(r.getEnrollFormula());
  740. mr.setDescription(r.getComment());
  741. ruleMatchList.add(mr);
  742. if(isScoreOnly) {
  743. mr.setType(VoluntaryDto.EnumRuleType.Readonly);
  744. mr.setValid(true);
  745. continue;
  746. }
  747. mr.setType(itemType2RuleType(r.getItemType(), r.getValueType(), false));
  748. if (VoluntaryDto.EnumRuleType.Readonly.equals(mr.getType())) {
  749. mr.setValid(true);
  750. } else {
  751. mr.setValid(processPassCheck(paramMap, formulaScoreStat, r.getValueRule(), mr, r.getValueType(), r.getItemField(), r.getValuePassRule()));
  752. }
  753. }
  754. }
  755. existSet.clear();
  756. Map<String, AMarjorPlan> historyPlanMap = historyPlanList.stream().collect(Collectors.toMap(t -> t.getYear() + t.getMajorName() + StringUtils.trimToEmpty(t.getMajorDirection()), Function.identity()));
  757. for (AMarjorSubmit s : validSubmitList) {
  758. if ("初录".equals(s.getEnrollType())) {
  759. VoluntaryDto.MajorEnrollHistory h = new VoluntaryDto.MajorEnrollHistory();
  760. h.setYear(s.getYear());
  761. h.setScore(null != s.getScore() ? String.valueOf(s.getScore()) : ""); // TODO 240301 直接输出录取分
  762. /*if (null != currTotal && null != s.getScore()) {
  763. Double diff = currTotal - s.getScore();
  764. h.setScore(diff > 0 ? "+" + diff.intValue() : "" + diff.intValue()); // TODO 需要一个分来比较,综合分?
  765. } else {
  766. h.setScore("");
  767. }*/
  768. AMarjorPlan p = historyPlanMap.get(s.getYear() + s.getMajorName() + StringUtils.trimToEmpty(s.getMajorDirection()));
  769. if(null != p) {
  770. h.setPlan(p.getPlanTotal()); // TODO 估计输出录取人数 23.12.26 分开输入录取和计划 24.12.27
  771. } else {
  772. // System.out.println(s.getId());
  773. }
  774. h.setEnroll(s.getEnrollTotal());
  775. histories.add(h);
  776. } else {
  777. /*VoluntaryDto.MajorClearingHistory c = new VoluntaryDto.MajorClearingHistory();
  778. c.setYear(s.getYear());
  779. if (null != currTotal && null != s.getScore()) {
  780. Double diff = currTotal - s.getScore();
  781. c.setScore(diff > 0 ? "+" + diff.intValue() : "" + diff.intValue()); // TODO 需要一个分来比较,综合分?
  782. } else {
  783. c.setScore("");
  784. }
  785. c.setRealNum(s.getEnrollTotal());
  786. clearings.add(c);*/ // TODO 240301 关闭补录
  787. }
  788. }
  789. VoluntaryDto.SingleResponse sr = new VoluntaryDto.SingleResponse();
  790. sr.setUniversityCode(u.getCode());
  791. sr.setMajorCode(null != prof ? prof.getCode() : "");
  792. sr.setMajorName(needMajor);
  793. sr.setMajorDirection(needDirect);
  794. // 查找最高的专业 EnrollRule 为本专业的,理论上只有一个合并rule有此值
  795. EnrollRateCalculator.RateLevel lastRl = null;
  796. boolean specialValid = true, specialPass = true;
  797. for (VoluntaryDto.MajorEnrollRule er : ruleMatchList) {
  798. if (null != er.getRl() && (null == lastRl || er.getRl().getRate() < lastRl.getRate())) {
  799. lastRl = er.getRl();
  800. }
  801. if ((specialPass || specialValid) && VoluntaryDto.EnumRuleType.Special.equals(er.getType())) {
  802. if (null == er.getValid()) {
  803. specialValid = false;
  804. } else if (!er.getValid()) {
  805. specialPass = false;
  806. }
  807. }
  808. }
  809. if (!specialPass) {
  810. sr.setEnrollRate(0);
  811. sr.setEnrollRateText("零概率");
  812. sr.setEnumPickEmpty(VoluntaryDto.EnumPickEmpty.EnrollPass);
  813. } else if (null != lastRl && specialValid) {
  814. sr.setEnumPickType(lastRl.getType());
  815. sr.setEnrollRate(lastRl.getRate());
  816. sr.setEnrollRateText(lastRl.getTypeLabel());
  817. } else {
  818. sr.setEnrollRateText("无概率");
  819. sr.setEnumPickEmpty(VoluntaryDto.EnumPickEmpty.New);
  820. }
  821. if (null != currPlan) {
  822. sr.setMajorGroup(currPlan.getMajorGroup()); // 没有MajorEnrollId, 暂时用这个
  823. sr.setMajorEnrollCode(StringUtils.isBlank(currPlan.getMajorEnrollCode()) ? String.valueOf(currPlan.getId()) : currPlan.getMajorEnrollCode());
  824. sr.setTips(null != currPlan ? currPlan.getComment() : "");
  825. }
  826. sr.setHistories(histories);
  827. sr.setClearings(clearings);
  828. sr.setRules(ruleMatchList);
  829. sr.setImproves(improveList);
  830. sr.setScore(currTotal);
  831. if (!isScoreOnly && null != lastSubmit && null != lastSubmit.getScore() && null != currTotal && currTotal < lastSubmit.getScore()) {
  832. Double diffValue = lastSubmit.getScore() - currTotal;
  833. sr.setImproves(formulaScoreStat.getImproveScore(diffValue));
  834. }
  835. return sr;
  836. }
  837. private void appendScoreRule(VoluntaryDto.FormulaScoreStat formulaScoreStat, Set<String> existSet, AEnrollScore r, List<AEnrollScore> fEnrollScoreList,
  838. Boolean isAi, Map<String, String> paramMap, Double inputScoreRate, Boolean isSkillScore) {
  839. String vt, iv;
  840. Double currScore = null;
  841. Double scoreRate = null;
  842. Double tmpValue; // 总分,得分
  843. if (null != (vt = r.getValueType()) && vt.startsWith("Score")) { //需要合并Score型(ScoreSingle, ScoreBase, ScoreSkill)的各类成绩, 其他为符合条件
  844. if (existSet.add(r.getEnrollFormula())) { // 合并规则只留第一个
  845. if (!existSet.add("_ScoreFormula_")) {
  846. existSet.remove(r.getEnrollFormula());
  847. return;
  848. }
  849. fEnrollScoreList.add(r);
  850. }
  851. if (null != r.getValueRule() && (scoreRate = Double.parseDouble(r.getValueRule())) <= 0.0) {
  852. scoreRate = 1.0;
  853. }
  854. formulaScoreStat.addTypeTotal(r.getScoreTotal());
  855. if (isAi && vt.equals("ScoreRate")) { // 根据得分率及总分计算分值
  856. currScore = isSkillScore ? 0.0 : r.getScoreTotal() * inputScoreRate;
  857. formulaScoreStat.addGroup(r.getItemGroup(), currScore, r.getScoreTotal().doubleValue(), scoreRate);
  858. formulaScoreStat.addType(r.getItemType(), r.getItemField(), currScore, scoreRate);
  859. } else {
  860. currScore = (!isSkillScore && StringUtils.isNotBlank((iv = paramMap.get(r.getItemField())))) ? NumberUtils.toDouble(iv, 0.0) : 0.0;
  861. formulaScoreStat.addGroup(r.getItemGroup(), currScore, r.getScoreTotal().doubleValue(), scoreRate);
  862. formulaScoreStat.addType(r.getItemType(), r.getItemField(), currScore, scoreRate);
  863. }
  864. } else {
  865. fEnrollScoreList.add(r);
  866. }
  867. }
  868. private VoluntaryDto.MajorEnrollRule buildEnrollRule(AEnrollScore r) {
  869. VoluntaryDto.MajorEnrollRule mr = new VoluntaryDto.MajorEnrollRule();
  870. mr.setCategory(VoluntaryDto.EnumRuleCategory.Enroll);
  871. mr.setType(itemType2RuleType(r.getItemType(), r.getValueType(), true)); // 学考 ScoreSingle 强转为 ScoreTotal, ScoreBase 校考, ScoreSkill 技能, Special 专项
  872. mr.setContent(r.getEnrollFormula());
  873. mr.setImproveType(r.getItemCategory());
  874. mr.setDescription(r.getComment());
  875. return mr;
  876. }
  877. private Pair<String, Set<String>> getGroupOptions(Map<String, Pair<String, Set<String>>> groupOptionsMap, Map<String, String> mutexOptionMap, Map<String, String> paramMap, String itemGroup) {
  878. String[] groupNameField = StringUtils.split(itemGroup, "_");
  879. String groupFieldName = groupNameField[groupNameField.length > 1 ? 1 : 0];
  880. Pair<String, Set<String>> pair = groupOptionsMap.get(groupFieldName);
  881. if(null == pair) {
  882. String iv = paramMap.get(groupFieldName);
  883. Set<String> options = StringUtils.isNotBlank(iv) ? JSONArray.parseArray(iv).stream().map(t -> (String) t).collect(Collectors.toSet()) : Sets.newHashSet();
  884. pair = new MutablePair<>(mutexOptionMap.get(groupFieldName), options);
  885. groupOptionsMap.put(groupFieldName, pair);
  886. }
  887. return pair;
  888. }
  889. private Boolean processPassCheck(Map<String, String> paramMap, VoluntaryDto.FormulaScoreStat stat, String valueRule,
  890. VoluntaryDto.MajorEnrollRule mr, String valueType, String itemField, String passRule) {
  891. Boolean matched = null;
  892. Double tmpValue;
  893. boolean isStat = null != valueType && valueType.startsWith("Stat");
  894. if (isStat || NumberTypeSet.contains(valueType)) {
  895. Double v = null;
  896. String iv;
  897. if (isStat) { // TODO 统计分
  898. v = stat.getTypeValue(valueType, itemField, "0".equals(valueRule));
  899. } else if(StringUtils.isNotBlank(passRule) && StringUtils.isNotBlank((iv = paramMap.get(itemField)))
  900. && (tmpValue = NumberUtils.toDouble(iv, 0.0)) > 0.01) {
  901. v = tmpValue;
  902. }
  903. if (v != null) {
  904. String[] range = passRule.split("-");
  905. if (StringUtils.isNotBlank(range[0]) && (tmpValue = NumberUtils.toDouble(range[0], 0.0)) > 0.01 && v < tmpValue) {
  906. matched = false;
  907. mr.setFailedMessage("小于最大值");
  908. } else if (range.length > 1 && StringUtils.isNotBlank(range[1]) && (tmpValue = NumberUtils.toDouble(range[1], 0.0)) > 0.01 && v > tmpValue) {
  909. matched = false;
  910. mr.setFailedMessage("超过最大值");
  911. } else {
  912. matched = true;
  913. }
  914. } else {
  915. mr.setFailedMessage("未输入参数");
  916. }
  917. } else if ("Radio".equals(valueType)) {
  918. String iv = paramMap.get(itemField);
  919. if (StringUtils.isNotBlank(iv)) {
  920. if (!(matched = StringUtils.isNotBlank(passRule) && passRule.contains(iv))) {
  921. mr.setFailedMessage("条件不满足");
  922. }
  923. } else {
  924. mr.setFailedMessage("未输入参数");
  925. }
  926. } else if ("Checkbox".equals(valueType) || "Picker".equals(valueType)) {
  927. String iv = paramMap.get(itemField);
  928. if(StringUtils.isBlank(passRule)) {
  929. if (!(matched = StringUtils.isBlank(iv))) {
  930. mr.setFailedMessage("选择条件不满足1");
  931. }
  932. } else if (StringUtils.isNotBlank(iv)) {
  933. List<String> valueList = Arrays.asList(passRule.split(","));
  934. List<String> inputList = JSONArray.parseArray(iv).stream().map(t -> (String) t).collect(Collectors.toList());
  935. if (!(matched = CollectionUtils.intersection(valueList, inputList).size() == valueList.size())) {
  936. mr.setFailedMessage("选择条件不满足2");
  937. }
  938. }
  939. }
  940. return matched;
  941. }
  942. private void sortAndExtractEnroll(List<VoluntaryDto.SingleResponse> singleResponseList, VoluntaryDto.AIResponse aiResp) {
  943. if (CollectionUtils.isEmpty(singleResponseList)) {
  944. aiResp.setEnrollRate(null);
  945. aiResp.setEnrollRateText("无概率");
  946. aiResp.setEnumPickEmpty(VoluntaryDto.EnumPickEmpty.EnrollPass);
  947. }
  948. Collections.sort(singleResponseList, new Comparator<VoluntaryDto.SingleResponse>() {
  949. @Override
  950. public int compare(VoluntaryDto.SingleResponse o1, VoluntaryDto.SingleResponse o2) {
  951. Integer d1 = o1.getEnrollRate();
  952. Integer d2 = o2.getEnrollRate();
  953. if (null == d1) {
  954. if (null != d2) {
  955. return 1;
  956. }
  957. return 0;
  958. } else if (null == d2) {
  959. return -1;
  960. }
  961. return -d1.compareTo(d2);
  962. }
  963. });
  964. for (VoluntaryDto.SingleResponse sr : singleResponseList) {
  965. aiResp.setEnrollRate(sr.getEnrollRate());
  966. aiResp.setEnrollRateText(sr.getEnrollRateText());
  967. aiResp.setEnumPickType(sr.getEnumPickType());
  968. aiResp.setEnumPickEmpty(sr.getEnumPickEmpty());
  969. break;
  970. }
  971. }
  972. public R<VoluntaryDto.VoluntaryConfig> getVoluntaryConfig() { // 如果有填报配置相关,放在这里
  973. VoluntaryDto.VoluntaryConfig resp = new VoluntaryDto.VoluntaryConfig();
  974. return R.ok(resp);
  975. }
  976. public List<JSONObject> getVoluntaryList(@ApiParam @RequestParam VoluntaryDto.EnumVoluntaryType type) { // 我的志愿表 // 后台填充快照缺省
  977. AWishRecord cond = new AWishRecord();
  978. cond.setUserId(SecurityUtils.getLoginUser().getUserId());
  979. if (null != type) {
  980. cond.setType(type.name());
  981. }
  982. cond.setStatus(1);
  983. List<AWishRecord> aWishRecordList = aWishRecordMapper.selectAWishRecordList(cond);
  984. List<JSONObject> dtoList = Lists.newArrayList();
  985. aWishRecordList.stream().forEach(t -> {
  986. dtoList.add(toModel(t));
  987. });
  988. return PageUtil.getDtoListWithPageable(aWishRecordList, dtoList);
  989. }
  990. public R<Long> submitVoluntary(JSONObject model) { // 填报 // 前端+后台按需要剔除一些不需快照的信息(目前主要是院校信息)
  991. SysUser user = SecurityUtils.getLoginUser().getUser();
  992. AWishRecord wishRecord = new AWishRecord();
  993. VoluntaryDto.User userSnapshot = new VoluntaryDto.User();
  994. userSnapshot.setName(user.getNickName());
  995. userSnapshot.setSex(user.getSex());
  996. userSnapshot.setExamType(user.getExamType().title()); // TODO MF
  997. userSnapshot.setProvinceName(user.getLocation());
  998. Long modelId = null;
  999. String modelName = null;
  1000. Integer year = null;
  1001. VoluntaryDto.EnumVoluntaryType type = null;
  1002. try {
  1003. modelId = model.getLong("id");
  1004. modelName = model.getString("name");
  1005. year = model.getInteger("year");
  1006. type = VoluntaryDto.EnumVoluntaryType.valueOf(model.getString("voluntaryType"));
  1007. wishRecord.setBatchName(model.getString("batchName"));
  1008. wishRecord.setRequest(model.getString("request"));
  1009. wishRecord.setDetails(model.getString("details"));
  1010. wishRecord.setUserSnapshot(mapper.writeValueAsString(userSnapshot));
  1011. } catch (JsonProcessingException e) {
  1012. return R.fail("格式错误: " + modelId);
  1013. }
  1014. wishRecord.setYear(null == year ? getPlanYear(user) : year);
  1015. wishRecord.setType(type.name());
  1016. if (null != modelId) {
  1017. wishRecord.setUpdateTime(new Date());
  1018. wishRecord.setId(modelId);
  1019. wishRecord.setName(modelName);
  1020. wishRecord.setUpdateTime(new Date());
  1021. aWishRecordMapper.updateAWishRecord(wishRecord);
  1022. } else {
  1023. wishRecord.setCreateTime(new Date());
  1024. wishRecord.setSeq(getWishSeq(wishRecord.getYear(), wishRecord.getType()));
  1025. wishRecord.setName(StringUtils.isNotBlank(modelName) ? modelName : ("AI".equals(wishRecord.getType()) ? "AI志愿" : "模拟志愿") + wishRecord.getSeq());
  1026. wishRecord.setUserId(user.getUserId());
  1027. wishRecord.setStatus(1);
  1028. aWishRecordMapper.insertAWishRecord(wishRecord);
  1029. }
  1030. return R.ok(wishRecord.getId());
  1031. }
  1032. private Integer getWishSeq(Integer year, String type) {
  1033. AWishRecord cond = new AWishRecord();
  1034. cond.setUserId(SecurityUtils.getLoginUser().getUserId());
  1035. cond.setYear(year);
  1036. cond.setType(type);
  1037. List<AWishRecord> aWishRecordList = aWishRecordMapper.selectAWishRecordList(cond);
  1038. Integer newSeq = CollectionUtils.isNotEmpty(aWishRecordList) ? aWishRecordList.get(0).getSeq() + 1 : 1;
  1039. return newSeq;
  1040. }
  1041. public void obsoleteWishRecord(Long userId) {
  1042. aWishRecordMapper.updateObsoleteByUser(userId);
  1043. busiWishRecordsMapper.updateObsoleteByUser(userId);
  1044. }
  1045. public R<JSONObject> getVoluntary(Long id) { // 志愿表详情 // 后台填充快照缺省
  1046. SysUser user = SecurityUtils.getLoginUser().getUser();
  1047. AWishRecord wishRecord = aWishRecordMapper.selectAWishRecordById(id);
  1048. if (null == wishRecord || !SecurityUtils.getUserId().equals(wishRecord.getUserId())) {
  1049. return R.fail("错误id");
  1050. }
  1051. if (null == wishRecord) {
  1052. throw new ServiceException("无此志愿id");
  1053. } else if (!user.getUserId().equals(wishRecord.getUserId())) {
  1054. throw new ServiceException("无此志愿id号");
  1055. }
  1056. JSONObject resp = toModel(wishRecord);
  1057. return R.ok(resp);
  1058. }
  1059. private JSONObject toModel(AWishRecord wishRecord) {
  1060. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  1061. JSONObject model = new JSONObject();
  1062. model.put("id", wishRecord.getId());
  1063. model.put("year", wishRecord.getYear());
  1064. model.put("voluntaryType", VoluntaryDto.EnumVoluntaryType.valueOf(wishRecord.getType()));
  1065. model.put("name", wishRecord.getName());
  1066. model.put("batchName", wishRecord.getBatchName());
  1067. model.put("createTime", sdf.format(wishRecord.getCreateTime()));
  1068. try {
  1069. model.put("userSnapshot", mapper.readValue(wishRecord.getUserSnapshot(), VoluntaryDto.User.class));
  1070. model.put("request", JSONObject.parseObject(wishRecord.getRequest()));
  1071. model.put("details", JSONArray.parseArray(wishRecord.getDetails()));
  1072. } catch (JsonProcessingException e) {
  1073. e.printStackTrace();
  1074. }
  1075. return model;
  1076. }
  1077. private VoluntaryDto.SingleResponse buildTestResp() {
  1078. String json = sysConfigService.selectConfigByKey("demo.voluntary.single");
  1079. if (StringUtils.isNotBlank(json)) {
  1080. try {
  1081. return mapper.readValue(json, VoluntaryDto.SingleResponse.class);
  1082. } catch (JsonProcessingException e) {
  1083. e.printStackTrace();
  1084. }
  1085. }
  1086. return new VoluntaryDto.SingleResponse();
  1087. }
  1088. private VoluntaryDto.AIResponse buildAiResp() {
  1089. String json = sysConfigService.selectConfigByKey("demo.voluntary.ai");
  1090. if (StringUtils.isNotBlank(json)) {
  1091. try {
  1092. VoluntaryDto.AIResponse resp = mapper.readValue(json, VoluntaryDto.AIResponse.class);
  1093. resp.setMajorDetails(Lists.newArrayList(buildTestResp()));
  1094. return resp;
  1095. } catch (JsonProcessingException e) {
  1096. e.printStackTrace();
  1097. }
  1098. }
  1099. return new VoluntaryDto.AIResponse();
  1100. }
  1101. private VoluntaryDto.VoluntaryModel buildModel() {
  1102. String json = sysConfigService.selectConfigByKey("demo.voluntary.model");
  1103. if(StringUtils.isNotBlank(json)) {
  1104. try {
  1105. VoluntaryDto.VoluntaryModel model = mapper.readValue(json, VoluntaryDto.VoluntaryModel.class);
  1106. model.setDetails(Lists.newArrayList(buildAiResp()));
  1107. return model;
  1108. } catch (JsonProcessingException e) {
  1109. e.printStackTrace();
  1110. }
  1111. }
  1112. return new VoluntaryDto.VoluntaryModel();
  1113. }
  1114. private VoluntaryDto.EnumRuleType itemType2RuleType(String itemType, String valueType, boolean foreSingleTotal) {
  1115. if ("Notice".equals(valueType)) {
  1116. return VoluntaryDto.EnumRuleType.Readonly;
  1117. } else if (foreSingleTotal && VoluntaryDto.EnumRuleType.ScoreSingle.name().equals(itemType)) {
  1118. return VoluntaryDto.EnumRuleType.ScoreTotal;
  1119. }
  1120. return VoluntaryDto.EnumRuleType.valueOf(itemType);
  1121. }
  1122. }