fix(lastleg): expose dispatch reviews and filter available capacity offers

This commit is contained in:
2026-10-08 14:11:55 +08:00
parent 1cb1f2d0c0
commit 136a2bc3b5
14 changed files with 194 additions and 18 deletions
@@ -7,7 +7,9 @@ public interface FleetCapacityOfferMapper extends BaseMapper<FleetCapacityOfferE
<script>
SELECT o.* FROM ll_fleet_capacity_offer o JOIN ll_fleet_trip t ON t.id=o.trip_id
<where>
<if test="salesOnly">o.status='OPEN'</if>
<if test="availableOnly">o.status='OPEN' AND t.status='PLANNED'
AND o.cutoff_at &gt; #{now} AND o.cutoff_at &lt;= t.planned_departure_at
</if>
<if test="q.status != null">AND o.status=#{q.status}</if>
<if test="q.tripNo != null and q.tripNo != ''">AND o.trip_no=#{q.tripNo}</if>
<if test="q.keyword != null and q.keyword != ''">AND (o.offer_no LIKE CONCAT('%',#{q.keyword},'%') OR o.trip_no LIKE CONCAT('%',#{q.keyword},'%'))</if>
@@ -21,7 +23,7 @@ public interface FleetCapacityOfferMapper extends BaseMapper<FleetCapacityOfferE
""")
com.baomidou.mybatisplus.extension.plugins.pagination.Page<FleetCapacityOfferEntity> pageOffers(
com.baomidou.mybatisplus.extension.plugins.pagination.Page<FleetCapacityOfferEntity> page,
@Param("q") FleetCapacityOfferModels.Query query,@Param("salesOnly") boolean salesOnly);
@Param("q") FleetCapacityOfferModels.Query query,@Param("availableOnly") boolean availableOnly,@Param("now") java.time.LocalDateTime now);
@Select("SELECT * FROM ll_fleet_capacity_offer WHERE offer_no=#{offerNo}")
FleetCapacityOfferEntity byNo(@Param("offerNo") String offerNo);
@Select("SELECT * FROM ll_fleet_capacity_offer WHERE offer_no=#{offerNo} FOR UPDATE")
@@ -16,8 +16,8 @@ public final class FleetCapacityOfferModels {
public record Query(@Min(1) Integer page,@Min(1) @Max(100) Integer size,
@Size(max=128) String keyword,@Size(max=128) String origin,@Size(max=128) String destination,
LocalDate departureFrom,LocalDate departureTo,@Pattern(regexp="DRAFT|OPEN|PAUSED|CLOSED") String status,
Boolean canConfirm,@Size(max=64) String tripNo) {
public Query {page=page==null?1:page;size=size==null?20:size;}
Boolean canConfirm,@Size(max=64) String tripNo,@Pattern(regexp="AVAILABLE|ALL") String scope) {
public Query {page=page==null?1:page;size=size==null?20:size;scope=scope==null?"AVAILABLE":scope;}
}
public record InquiryQuery(@Min(1) Integer page,@Min(1) @Max(100) Integer size,
@Size(max=128) String keyword,@Size(max=64) String offerNo,@Size(max=32) String status,
@@ -52,7 +52,8 @@ public class FleetCapacityOfferService {
@Transactional(readOnly=true,isolation=Isolation.REPEATABLE_READ)
public PageResult<FleetCapacityOfferModels.Offer> page(FleetCapacityOfferModels.Query query){
readPermission();
var result=offers.pageOffers(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(query.canConfirm()==null?query.page():1,query.canConfirm()==null?query.size():100),query,!manage());
if("ALL".equals(query.scope())&&!manage())throw forbidden();
var result=offers.pageOffers(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(query.canConfirm()==null?query.page():1,query.canConfirm()==null?query.size():100),query,!"ALL".equals(query.scope()),LocalDateTime.ofInstant(clock.instant(),ZoneOffset.UTC));
if(query.canConfirm()!=null&&result.getTotal()>100)throw bad("实时接单资格筛选最多核对100趟车次,请缩小起运仓、目的地或日期范围");
var values=result.getRecords().stream().map(row->project(row,trip(row.getTripNo(),false)))
.filter(value->query.canConfirm()==null||query.canConfirm()==value.canConfirm()).toList();
@@ -0,0 +1,43 @@
package com.baiyue.lastleg.fleet.trip;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.stream.Collectors;
/** Read existing reviews in batches; never infer approval from dispatch execution status. */
@Service
public class FleetDispatchWorkflowQuery {
public record Review(String status, String reason, java.time.Instant reviewedAt) { }
public record Workflow(String tripNo, Review dispatchReview, Review loadingReview) { }
private final FleetTripDispatchMapper bindings;
private final FleetTripMapper trips;
private final ObjectMapper json;
public FleetDispatchWorkflowQuery(FleetTripDispatchMapper bindings, FleetTripMapper trips, ObjectMapper json) {
this.bindings = bindings; this.trips = trips; this.json = json;
}
public Map<String, Workflow> read(List<String> dispatchNos) {
if (dispatchNos.isEmpty()) return Map.of();
var members = bindings.findByDispatchNos(dispatchNos);
if (members.isEmpty()) return Map.of();
var parents = trips.selectBatchIds(members.stream().map(FleetTripDispatch::getTripId).distinct().toList())
.stream().collect(Collectors.toMap(FleetTripEntity::getId, trip -> trip));
Map<String, Workflow> result = new HashMap<>();
for (var member : members) {
var trip = parents.get(member.getTripId());
if (trip == null) throw new IllegalStateException("关联车次不存在");
result.put(member.getDispatchNo(), new Workflow(member.getTripNo(),
review(member.getReviewJson()), review(trip.getLoadingReviewJson())));
}
return result;
}
private Review review(String value) {
var state = FleetTripReview.read(json, value);
return new Review(state.status(), state.reason(), state.reviewedAt());
}
}