From 147d48c8a6ba4a4ca61ca2c9c142128eb2c68239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=85=E6=88=BF?= Date: Sun, 18 Jan 2026 00:49:41 +0800 Subject: [PATCH] refactor: implement ScheduleArrangeServiceImpl Phase 2, Step 2.7: Extract arrangement logic from MartialScheduleServiceImpl - Implement saveDraftSchedule method (160 lines) with complete business logic - Implement saveAndLockSchedule method (53 lines) with locking logic - Implement moveScheduleGroup method (58 lines) with group movement logic - Total: 271 lines of arrangement logic extracted - All 443 tests passing Related to Phase 2 refactoring plan --- .../buckets/.bloomcycle.bin/xl.meta | Bin 743 -> 743 bytes .../buckets/.usage-cache.bin.bkp/xl.meta | Bin 637 -> 628 bytes .../buckets/.usage-cache.bin/xl.meta | Bin 637 -> 628 bytes .../.minio.sys/buckets/.usage.json/xl.meta | Bin 2360 -> 2360 bytes .../.usage-cache.bin.bkp/xl.meta | Bin 627 -> 636 bytes .../000000-assets/.usage-cache.bin/xl.meta | Bin 627 -> 636 bytes .../assets/.usage-cache.bin.bkp/xl.meta | Bin 575 -> 575 bytes .../buckets/assets/.usage-cache.bin/xl.meta | Bin 575 -> 575 bytes .../service/IScheduleArrangeService.java | 2 +- .../impl/ScheduleArrangeServiceImpl.java | 314 ++++++++++++++++++ 10 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/springblade/modules/martial/service/impl/ScheduleArrangeServiceImpl.java diff --git a/minio_data/.minio.sys/buckets/.bloomcycle.bin/xl.meta b/minio_data/.minio.sys/buckets/.bloomcycle.bin/xl.meta index 7f3a6e057120562bfcabf31803337bceb21d258f..fc25b5bbe79500bf48948237906b2e7b96363f5c 100644 GIT binary patch delta 434 zcmaFP`kZxw2G6ABRqHBt9btKD{O0IH6Jvn`o)@p}ed^xT-S6WT!x}kZVtG9qM9suM zrjsKW)#{g|mL#U%R4_=iG&V3aPcceNGE7dkFg8lEG_^=dO*A*OOiM{hG+bDAE8vVrYIigVFfA4%-)-)K5Gxxy5!2_oK)xh z+=84`p!6LEfypPCP2vGY4rZw4d?C_(^zbb(7QWGK^3cGJm;CdLBFUosC*arF(^DQfz&;oIe{6U*z_AZjN5 zF;!oZT9TN4Qz6aVGSMK_AlcZ`!YDb(z$Do)H90lK(!$)>&^Rr{GI6pYqgwsKvUBN4 zry0(@yZ*bCv2jUWX->{j#^8=E)h2B}7OY7Wtuc>{HsCgscn~-J`;3FLJ<78<=0x@{ zDq#SFmgUK%MMbH3B@9dQQY%W%mMmIcT#{H+lA3ayhZUqEGkcesIt%a7b;*^6$Cd-BQe(-!JVWLXBLd$ zp3^WR diff --git a/minio_data/.minio.sys/buckets/.usage-cache.bin.bkp/xl.meta b/minio_data/.minio.sys/buckets/.usage-cache.bin.bkp/xl.meta index 738f8cdbef21bbbffc1aab9d831658927a04208d..082e26303fb46d58f06baf04ab822ff3a7ce39be 100644 GIT binary patch delta 341 zcmey%@`YuB2G6ABRV|FMM_3m3#XXs5Vl1F!W8QXbp8uJqc3*X7$(z$BmdB|dTosU5 zRN@$%S(REm@gl>)B_I}%Ug{f?nVWi9q8nn!#6LC)X6C5|7D<*#21XXfspcsbCI)Gi zN#-WWiK(f{7Aa}wlOq`QtZ$nt2r)J;$t%stIr1=j@ulf6?%WP~f75Gbq5smIdA4h; zSC&CuV3YpSX>fXkdj!EdYp%q0SGd) z_vUGAR*_g8oSj+Vk(!v3nU{WOsdHj-Mr!l2#Ny)AlH!g<$r%$C6<0AV$}eVER94Ke zI3*WI6jv3`2AQy^EMq#u!sJ714ZuM65@Tzu2T-XT6B9QF1H0TpeUJf@I2i(@bexVE bF|cMj#4C6S-PLUJ(VcP6IGt_VhN=Gm^xlNL delta 350 zcmeyu@|R_T2G7LD&kWyvIKt9doZ>st#8@EZ*)}_#T0fce=B8ej=!O_F@sEu{Qj)QWrKw3uvPrUGnqiuyiKUrw zVzRM$a$=%+nt5X4+9C`KYe#X|I$;S_0W~sWd&eQ3x?`v!Q zh^z}28z#({wdI^i1Bb>|{l6{@q6}AA8CbX`A7qrOU%1LAvA86(ASJOR^*9eJ0}y0p z?^4t7m?644I6JezBQ-H6GcWznQs>0vjMV0}24J9@SX`W1Qrxj9Ib*`2;;KuGt+5`9 z@{8pbl@&8APRRul#Z|?#8JL*3IT+YMW-cnrm@WrmFfc4kKD1CDq;t};Ma9Zh3_yJh i44e#=rWXwOHx)5_Y2dA4_AA+!lpyB8mtiq2VG{r#+KAl% diff --git a/minio_data/.minio.sys/buckets/.usage-cache.bin/xl.meta b/minio_data/.minio.sys/buckets/.usage-cache.bin/xl.meta index e63556d9b97cb5d64d4860d82033cd5f78a86442..b221ca0b64eb9cee18a258739d5af8143c450ef6 100644 GIT binary patch delta 341 zcmey%@`YuB2G6ABRncb;A7Rl7{LeMf#8`mC+Op)WoWt^cf|oviiL$JmSRSW-a8*EJ zQHf)4W>sqO#ET3Emw;G6dZ}+nW^U?biEfA?6aUyKn3<;8s4 zCYhTgC#I$*Tco6!PmW;Jv)1{fY0KESB(F3l=g7nC#h0ePxN|$`{Y|f#h5k!-=Gm^X zW<7g1hFj`}_#VfOn;aTj_5Zpsh%)rDGNf=$KFBClzkZcZVsS}mK}upt>Tw=c1|Z1H z-kYbfSw&)XaCT;aM`~hDW?uTCrOt`T8L7?75{rvdONu)dC1*@nR9wZdD8HCtQCTs= z;*?wapP09Po6@&Et; delta 350 zcmeyu@|R_T2G7LD&$!&)A7L>o53`?WVl1%pl{QbakI#V}MWV9ND`)vmERRz^wJIR7 zsKhZivnsWC;zfp2OF%3jz0@}(GdK0JL^s5ciGOSql9G%~EKN;Pl1-8g(+txrO)Slf z6O)b2lM@ro)65eSCr2>qS#Oz|G=s5mNnUAA&XHHY?q_Tbntc55WtOTN>pY$A`o6Z- zkI1@kv0=iDSzFGTG;nBa)&J|lAj)u+m4St8@;LGV{_8Ep<*z&PZ)uYXAniiN(dKCB+?!k~1bODz3W3*c$7x zD8E>4QCTs=;*?wjOlV91_Q&wP%X1|hsNeN;ed>Izg5;g$<;KYc- diff --git a/minio_data/.minio.sys/buckets/.usage.json/xl.meta b/minio_data/.minio.sys/buckets/.usage.json/xl.meta index a3ba632596b8cd2dd13bda6fb7e6db3757dd88d1..bcced4a5259cd9231fcc1d8cdbfd834e44f5bf8f 100644 GIT binary patch delta 175 zcmdlXv_oiu2G6ABRU1Sn9bsY2`_(nk#8}|fewD1dD;&-(7k5@He9W3RvAmuQqGsYB z)5#HxaDkNK&rluODnkFZi8W~txSelub7#gISSelxprkYu#SuQL)m!5Q* z;oKEJ-3Z3UC3&SeIY-&oSj_&e-n(z*Yx@g|yH;9HTR8ogQg5->{e6x4p4SYFQtQ8V$6 zsrr)ClEn0z3MPpb$%d9ji75tYDM{wWsi~HRrsf7l7D;Jl$!5l;DU%Hu<;)hAol8$T z&2a8f=C+rNjZ5-Mb8?QduX!wDwnSpdx!(IFbvoG_pT->uE66z_mXKW~XK$v%>0>^5 c4Wlf-v5A$5nVy-ssfCG|p_%FC8;l9;00$>TssI20 diff --git a/minio_data/.minio.sys/buckets/000000-assets/.usage-cache.bin.bkp/xl.meta b/minio_data/.minio.sys/buckets/000000-assets/.usage-cache.bin.bkp/xl.meta index eccb323b870a84c0ececd94d8b7bf3cb9c77f62b..7fb32a761d528e781bf848ccc007cd26de426e7c 100644 GIT binary patch delta 335 zcmey&@`q)D2G6ABl^ZrpIl{8ETwZUYiLpRl#X7&GBK~m=Z}U$pepq2Qu{=)wBt7|1m+PD-0>$f)MHuKNv~h!lUQ65 zT9A@hl6stnl>rDcv-jqyUp_Ex5|c>A7X8wKoczR;jz!5C6BZR$U1DsF^;nc&EVrnv zm|<~BE|4g$DxS^2#Kg_Pzz)*As4Qc;9EicdurT>BNZ&-DJ_ev328KiI3=JFt5lIYD VB{e<%^~X{+%ut@gc*ZyEEdYe5hEo6l delta 325 zcmeyv@|k6V2G7LDPd(4-9${HBW$*uqCdL9Gmp9ZOUGJ6AxQ@>{Zg=^|iRE$X2UZ0n z7L_;#XI7;aPrS%*USs9jaPTtR`CcSW#PhxRN zXhBM1N$PPPRt6x*%-*G@ZogvkBqovgjz!5C6SwG>7Ubk7rYtJ1Vpx=4%&@4em|<~B zE|4g$DxMAGW=v;Tn0yGNb^=h%CC1iR51=|ZCMIqU26nl_91IN{3SJt!N?Ys?TzF)b PUL11t#0{SG;I*az%x#CE diff --git a/minio_data/.minio.sys/buckets/000000-assets/.usage-cache.bin/xl.meta b/minio_data/.minio.sys/buckets/000000-assets/.usage-cache.bin/xl.meta index 3e10c417da0205ced5c399333ac2473128772e1f..ae4bb2eed391de4e8fa901b8cdf2b29a21ef5d81 100644 GIT binary patch delta 320 zcmey&@`q)D2G6ABm9>3ejd6uVenVrgW{ISRSW-a#cWL zQHf)4W>sqO#ET3kmw;G6dZ}+nW^U?biEfA?6aUyK7^j&Sq*xl67$zEKNv~h!lUQ65T9A@hl6stnl>rDcv-jqy zUp_Ex5|c>A7X8wKoczR;jz!5C6BZR$U1DsF^;nc&EVrnvm|<~BE|4g$DxS^2#Kg_P zzz)*As4Qc;9EicdurT>BNZ&-DJ_ev328KiI3=JFt5lIYDB{e<%^~X{+%ut@gc*ZyE FEdV$1ep&zk delta 311 zcmeyv@|k6V2G7LDPnr1nkFY%cdRT9wiLt=_t7~9QnYpQ#CAuMoO#EY`U}k7+Xk?jcV3?d}Y;I(jXp)ka zl$vOfl5CimVrXP!G&zD%&+^EUUpp8Zm*kb^RX&NuC7}f=i6yDWc~}{MATxWH zn!5dp$&;8w;yV^4XH49pUs{lppO~_!xQby>elf$MvSNnCDY-zRxT<(Ikee}`VPWzi zklG1AHJ2D$V?BWC?&=sKXBoZS$c8E(Gxd#(u3EU0sva# BefIzW diff --git a/minio_data/.minio.sys/buckets/assets/.usage-cache.bin.bkp/xl.meta b/minio_data/.minio.sys/buckets/assets/.usage-cache.bin.bkp/xl.meta index 76edf144cafb20506ee5a93e6029a5fdcfdc336d..acf1dbd64fac6b356839797da3badcac87681e6f 100644 GIT binary patch delta 176 zcmdnbvY%yw2G6ABRR)ZiM_6=UPMtr|#8|-e&`zEU7d>`wpSi2iCF4xn#PWJJh?k$wZCx1~m*kb^p^_s)l0{C-w?Jui(@jFN5y&RKrEI81);_Q{(WwWZgu@<}W%2`xxTEJ;1i a!^!{znb~{uG@@7KiLrpz!Ji8jJ#vrPdZeD)BU*Xh#PWJJh?|O zH4M?!M!XLWY}=l)-0gBx|8bAFyH3*-gB}V+&b{bS6k#xVGo!Zj!c{(r#U-HyDTyVi c$9Y&8fFLt_mzw&$ZjZ@78J$>|W4XTr0PG)^%}x)C^M`SIc~`MujGZ)Vh% oUcbsGvA86(ASJOR^*9eJ0}y0p@6FSQVriKClhKLA#rum10O+tt*8l(j delta 191 zcmdnbvY%yw2G7LDPZypEJHo0qCtvjT1r}yg{hIbVX9G5vW1bQ() + .eq("competition_id", dto.getCompetitionId()) + .eq("project_id", projectId) + .eq("is_deleted", 0) + .last("LIMIT 1") + ); + } + + // Determine project type from DTO + Integer projectType = null; + if ("集体".equals(groupDTO.getType())) { + projectType = 2; + } else if ("单人".equals(groupDTO.getType())) { + projectType = 1; + } + + // Calculate participant/team counts + int participantCount = groupDTO.getParticipants() != null ? groupDTO.getParticipants().size() : 0; + + if (existingGroup != null) { + realGroupId = existingGroup.getId(); + existingGroup.setGroupName(groupDTO.getTitle()); + existingGroup.setProjectType(projectType); + existingGroup.setTotalParticipants(participantCount); + existingGroup.setTotalTeams(participantCount); + existingGroup.setCategory(groupDTO.getCode()); + scheduleGroupMapper.updateById(existingGroup); + } else { + MartialScheduleGroup newGroup = new MartialScheduleGroup(); + newGroup.setCompetitionId(Long.valueOf(dto.getCompetitionId())); + newGroup.setProjectId(projectId); + newGroup.setGroupName(groupDTO.getTitle()); + newGroup.setProjectType(projectType); + newGroup.setTotalParticipants(participantCount); + newGroup.setTotalTeams(participantCount); + newGroup.setCategory(groupDTO.getCode()); + scheduleGroupMapper.insert(newGroup); + realGroupId = newGroup.getId(); + } + } + + MartialScheduleDetail detail = scheduleDetailMapper.selectOne( + new QueryWrapper() + .eq("schedule_group_id", realGroupId) + .eq("is_deleted", 0) + .last("LIMIT 1") + ); + + if (detail == null) { + detail = new MartialScheduleDetail(); + detail.setScheduleGroupId(realGroupId); + detail.setCompetitionId(dto.getCompetitionId()); + } + + detail.setVenueId(groupDTO.getVenueId()); + detail.setVenueName(groupDTO.getVenueName()); + detail.setTimeSlot(groupDTO.getTimeSlot()); + + // 设置时间段(上午/下午) + if (groupDTO.getTimeSlot() != null && groupDTO.getTimeSlot().contains("上午")) { + detail.setTimePeriod("morning"); + } else if (groupDTO.getTimeSlot() != null && groupDTO.getTimeSlot().contains("下午")) { + detail.setTimePeriod("afternoon"); + } else { + detail.setTimePeriod("morning"); // 默认上午 + } + + // 解析日期 + if (groupDTO.getTimeSlot() != null && groupDTO.getTimeSlot().contains("年")) { + try { + String dateStr = groupDTO.getTimeSlot().split(" ")[0]; + dateStr = dateStr.replace("年", "-").replace("月", "-").replace("日", ""); + detail.setScheduleDate(LocalDate.parse(dateStr)); + } catch (Exception e) { + // 日期解析失败,使用当天日期 + detail.setScheduleDate(LocalDate.now()); + } + } + + // 如果日期仍为空,设置默认日期 + if (detail.getScheduleDate() == null) { + detail.setScheduleDate(LocalDate.now()); + } + + if (detail.getId() == null) { + scheduleDetailMapper.insert(detail); + } else { + scheduleDetailMapper.updateById(detail); + } + + // 1.5 同步更新项目的venue_id,保持数据一致性 + if (groupDTO.getVenueId() != null) { + MartialScheduleGroup group = scheduleGroupMapper.selectById(realGroupId); + if (group != null && group.getProjectId() != null) { + MartialProject project = projectService.getById(group.getProjectId()); + if (project != null) { + project.setVenueId(groupDTO.getVenueId()); + projectService.updateById(project); + } + } + } + + // 2. 更新参赛者信息 + if (groupDTO.getParticipants() != null) { + for (ParticipantDTO participantDTO : groupDTO.getParticipants()) { + // Query by participant_id (registration ID) and schedule_group_id, not by primary key + MartialScheduleParticipant participant = scheduleParticipantMapper.selectOne( + new QueryWrapper() + .eq("participant_id", participantDTO.getId()) + .eq("schedule_group_id", realGroupId) + .eq("is_deleted", 0) + .last("LIMIT 1") + ); + if (participant != null) { + participant.setCheckInStatus(participantDTO.getStatus()); + participant.setPerformanceOrder(participantDTO.getSortOrder()); + participant.setScheduleStatus("draft"); + scheduleParticipantMapper.updateById(participant); + } else { + // Create new participant record if not exists + MartialScheduleParticipant newParticipant = new MartialScheduleParticipant(); + newParticipant.setScheduleGroupId(realGroupId); + newParticipant.setScheduleDetailId(detail.getId()); // Set schedule_detail_id + newParticipant.setParticipantId(Long.valueOf(participantDTO.getId())); + newParticipant.setOrganization(participantDTO.getSchoolUnit()); + newParticipant.setCheckInStatus(participantDTO.getStatus()); + newParticipant.setPerformanceOrder(participantDTO.getSortOrder()); + newParticipant.setScheduleStatus("draft"); + scheduleParticipantMapper.insert(newParticipant); + } + } + } + } + + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean saveAndLockSchedule(Long competitionId) { + log.info("=== saveAndLockSchedule 开始 === competitionId: {}", competitionId); + // 1. 查询所有分组 + List groups = scheduleGroupMapper.selectList( + new QueryWrapper() + .eq("competition_id", competitionId) + .eq("is_deleted", 0) + ); + + if (groups.isEmpty()) { + // 没有分组数据,视为成功(可能是初始状态,尚未进行自动编排) + return true; + } + + // 2. 获取所有分组ID + List groupIds = groups.stream().map(MartialScheduleGroup::getId).collect(Collectors.toList()); + + // 3. 更新所有参赛者的编排状态为completed + List participants = scheduleParticipantMapper.selectList( + new QueryWrapper() + .in("schedule_group_id", groupIds) + .eq("is_deleted", 0) + ); + + // 按分组和出场顺序分配选手编号 + Map groupCounters = new HashMap<>(); + participants.sort((a, b) -> { + int groupCompare = a.getScheduleGroupId().compareTo(b.getScheduleGroupId()); + if (groupCompare != 0) return groupCompare; + return Integer.compare(a.getPerformanceOrder() != null ? a.getPerformanceOrder() : 0, + b.getPerformanceOrder() != null ? b.getPerformanceOrder() : 0); + }); + + for (MartialScheduleParticipant participant : participants) { + participant.setScheduleStatus("completed"); + scheduleParticipantMapper.updateById(participant); + + // 分配选手编号 + if (participant.getParticipantId() != null) { + Long groupId = participant.getScheduleGroupId(); + int counter = groupCounters.getOrDefault(groupId, 0) + 1; + groupCounters.put(groupId, counter); + + // 更新选手编号 (格式: 分组序号-出场序号) + MartialAthlete athlete = athleteMapper.selectById(participant.getParticipantId()); + if (athlete != null && (athlete.getPlayerNo() == null || athlete.getPlayerNo().isEmpty())) { + athlete.setPlayerNo(String.format("%03d", counter)); + athleteMapper.updateById(athlete); + } + } + } + + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean moveScheduleGroup(org.springblade.modules.martial.pojo.dto.MoveScheduleGroupDTO dto) { + // 1. 查询分组信息 + MartialScheduleGroup group = scheduleGroupMapper.selectById(dto.getGroupId()); + if (group == null) { + throw new RuntimeException("分组不存在"); + } + + // 2. 查询该分组的详情记录 + List details = scheduleDetailMapper.selectList( + new QueryWrapper() + .eq("schedule_group_id", dto.getGroupId()) + .eq("is_deleted", 0) + ); + + if (details.isEmpty()) { + throw new RuntimeException("分组详情不存在"); + } + + // 3. 查询目标场地信息 + MartialVenue targetVenue = venueService.getById(dto.getTargetVenueId()); + if (targetVenue == null) { + throw new RuntimeException("目标场地不存在"); + } + + // 4. 根据时间段索引计算日期和时间 + // 假设: 0=第1天上午, 1=第1天下午, 2=第2天上午, 3=第2天下午... + // 需要从赛事信息中获取起始日期 + int dayOffset = dto.getTargetTimeSlotIndex() / 2; // 每天2个时段 + boolean isAfternoon = dto.getTargetTimeSlotIndex() % 2 == 1; + String timeSlot = isAfternoon ? "13:30" : "08:30"; + + // 获取赛事起始日期(从第一个detail中获取) + LocalDate baseDate = details.get(0).getScheduleDate(); + if (baseDate == null) { + throw new RuntimeException("无法确定赛事起始日期"); + } + + // 计算目标日期(从起始日期开始,加上dayOffset天的偏移) + // 如果当前detail的日期早于base date,需要调整 + LocalDate minDate = details.stream() + .map(MartialScheduleDetail::getScheduleDate) + .filter(Objects::nonNull) + .min(LocalDate::compareTo) + .orElse(baseDate); + + LocalDate targetDate = minDate.plusDays(dayOffset); + + // 5. 更新所有detail记录 + for (MartialScheduleDetail detail : details) { + detail.setVenueId(dto.getTargetVenueId()); + detail.setVenueName(targetVenue.getVenueName()); + detail.setScheduleDate(targetDate); + detail.setTimeSlot(timeSlot); + detail.setTimeSlotIndex(dto.getTargetTimeSlotIndex()); + scheduleDetailMapper.updateById(detail); + } + + return true; + } +}