|
|
@@ -1,30 +1,50 @@
|
|
|
package org.dromara.main.service.impl;
|
|
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
+import cn.hutool.crypto.digest.DigestUtil;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
+import org.dromara.common.core.exception.ServiceException;
|
|
|
import org.dromara.common.core.utils.MapstructUtils;
|
|
|
import org.dromara.common.core.utils.StringUtils;
|
|
|
+import org.dromara.common.json.utils.JsonUtils;
|
|
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
|
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
|
|
+import org.dromara.main.domain.MainBackCandidate;
|
|
|
+import org.dromara.main.domain.MainBackCategory;
|
|
|
+import org.dromara.main.domain.MainBackClause;
|
|
|
import org.dromara.main.domain.MainBackOrder;
|
|
|
import org.dromara.main.domain.MainBackRecord;
|
|
|
import org.dromara.main.domain.MainOrder;
|
|
|
import org.dromara.main.domain.bo.MainBackOrderBo;
|
|
|
+import org.dromara.main.domain.bo.PortalCheckOrderCreateBo;
|
|
|
+import org.dromara.main.domain.MainStudent;
|
|
|
+import org.dromara.main.domain.Payment;
|
|
|
+import org.dromara.main.domain.vo.MainBackClauseVo;
|
|
|
+import org.dromara.main.domain.vo.MainBackOrderCandidateVo;
|
|
|
import org.dromara.main.domain.vo.MainBackOrderVo;
|
|
|
+import org.dromara.main.mapper.MainBackCandidateMapper;
|
|
|
+import org.dromara.main.mapper.MainBackCategoryMapper;
|
|
|
+import org.dromara.main.mapper.MainBackClauseMapper;
|
|
|
import org.dromara.main.mapper.MainBackOrderMapper;
|
|
|
import org.dromara.main.mapper.MainBackRecordMapper;
|
|
|
import org.dromara.main.mapper.MainOrderMapper;
|
|
|
+import org.dromara.main.mapper.MainStudentMapper;
|
|
|
+import org.dromara.main.mapper.PaymentMapper;
|
|
|
import org.dromara.main.service.IMainBackOrderService;
|
|
|
import org.dromara.system.domain.bo.SysTenantBo;
|
|
|
import org.dromara.system.domain.vo.SysTenantVo;
|
|
|
import org.dromara.system.service.ISysTenantService;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.Comparator;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Date;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
@@ -37,6 +57,11 @@ public class MainBackOrderServiceImpl implements IMainBackOrderService {
|
|
|
private final MainBackOrderMapper baseMapper;
|
|
|
private final MainBackRecordMapper recordMapper;
|
|
|
private final MainOrderMapper mainOrderMapper;
|
|
|
+ private final MainBackCategoryMapper mainBackCategoryMapper;
|
|
|
+ private final MainBackClauseMapper mainBackClauseMapper;
|
|
|
+ private final MainBackCandidateMapper mainBackCandidateMapper;
|
|
|
+ private final MainStudentMapper mainStudentMapper;
|
|
|
+ private final PaymentMapper paymentMapper;
|
|
|
private final ISysTenantService tenantService;
|
|
|
|
|
|
@Override
|
|
|
@@ -124,6 +149,176 @@ public class MainBackOrderServiceImpl implements IMainBackOrderService {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public MainBackOrderVo queryPortalDetail(Long orderId, String tenantId) {
|
|
|
+ MainOrder mainOrder = mainOrderMapper.selectById(orderId);
|
|
|
+ if (mainOrder == null || !StringUtils.equals(mainOrder.getTenantId(), tenantId)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (mainOrder.getBusinessId() == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ MainBackOrder backOrder = baseMapper.selectById(mainOrder.getBusinessId());
|
|
|
+ if (backOrder == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ MainBackOrderVo vo = MapstructUtils.convert(backOrder, MainBackOrderVo.class);
|
|
|
+ fillPortalFields(vo, mainOrder);
|
|
|
+ vo.setPayment(paymentMapper.selectVoOne(
|
|
|
+ Wrappers.<Payment>lambdaQuery()
|
|
|
+ .eq(Payment::getOrderId, orderId)
|
|
|
+ .orderByDesc(Payment::getCreateTime)
|
|
|
+ .last("limit 1"),
|
|
|
+ false
|
|
|
+ ));
|
|
|
+ vo.setCandidates(buildPortalCandidates(backOrder.getId()));
|
|
|
+ vo.setClauses(buildPortalClauses(backOrder));
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public MainBackOrderVo createPortalOrder(String tenantId, PortalCheckOrderCreateBo bo) {
|
|
|
+ if (StringUtils.isBlank(tenantId)) {
|
|
|
+ throw new ServiceException("未登录或token已失效");
|
|
|
+ }
|
|
|
+ if (bo.getCandidates() == null || bo.getCandidates().isEmpty()) {
|
|
|
+ throw new ServiceException("候选人列表不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
|
|
|
+ if (tenant == null) {
|
|
|
+ throw new ServiceException("企业信息不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Long> clauseIds;
|
|
|
+ String clauseIdsJson;
|
|
|
+ String clauseGroupKey = null;
|
|
|
+ Long categoryId = null;
|
|
|
+ String categoryName;
|
|
|
+ BigDecimal unitPrice;
|
|
|
+
|
|
|
+ if (Integer.valueOf(1).equals(bo.getOrderType())) {
|
|
|
+ if (bo.getCategoryId() == null) {
|
|
|
+ throw new ServiceException("套餐ID不能为空");
|
|
|
+ }
|
|
|
+ MainBackCategory category = mainBackCategoryMapper.selectById(bo.getCategoryId());
|
|
|
+ if (category == null || !"1".equals(category.getStatus())) {
|
|
|
+ throw new ServiceException("背调套餐不存在或已停用");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<MainBackClause> clauses = mainBackClauseMapper.selectList(
|
|
|
+ Wrappers.<MainBackClause>lambdaQuery()
|
|
|
+ .eq(MainBackClause::getCategoryId, category.getId())
|
|
|
+ .eq(MainBackClause::getStatus, "1")
|
|
|
+ .orderByAsc(MainBackClause::getId)
|
|
|
+ );
|
|
|
+ if (clauses.isEmpty()) {
|
|
|
+ throw new ServiceException("该套餐下暂无可用条款");
|
|
|
+ }
|
|
|
+
|
|
|
+ categoryId = category.getId();
|
|
|
+ categoryName = category.getName();
|
|
|
+ unitPrice = defaultAmount(category.getPrice());
|
|
|
+ clauseIds = clauses.stream().map(MainBackClause::getId).toList();
|
|
|
+ clauseIdsJson = JsonUtils.toJsonString(clauseIds);
|
|
|
+ } else if (Integer.valueOf(2).equals(bo.getOrderType())) {
|
|
|
+ if (StringUtils.isBlank(bo.getClauseIds())) {
|
|
|
+ throw new ServiceException("自定义条款不能为空");
|
|
|
+ }
|
|
|
+ List<Long> parsedClauseIds = JsonUtils.parseArray(bo.getClauseIds(), Long.class);
|
|
|
+ if (parsedClauseIds == null || parsedClauseIds.isEmpty()) {
|
|
|
+ throw new ServiceException("自定义条款不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ clauseIds = parsedClauseIds.stream().distinct().sorted().collect(Collectors.toList());
|
|
|
+ List<MainBackClause> clauses = mainBackClauseMapper.selectBatchIds(clauseIds).stream()
|
|
|
+ .filter(clause -> clause != null && "1".equals(clause.getStatus()))
|
|
|
+ .sorted(Comparator.comparing(MainBackClause::getId))
|
|
|
+ .toList();
|
|
|
+ if (clauses.size() != clauseIds.size()) {
|
|
|
+ throw new ServiceException("部分自定义条款不存在或已停用");
|
|
|
+ }
|
|
|
+
|
|
|
+ unitPrice = clauses.stream()
|
|
|
+ .map(MainBackClause::getPrice)
|
|
|
+ .filter(java.util.Objects::nonNull)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+ categoryName = "自定义背调组合";
|
|
|
+ clauseIdsJson = JsonUtils.toJsonString(clauseIds);
|
|
|
+ clauseGroupKey = DigestUtil.md5Hex(clauseIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
|
|
+ } else {
|
|
|
+ throw new ServiceException("不支持的订单类型");
|
|
|
+ }
|
|
|
+
|
|
|
+ int candidateCount = bo.getCandidates().size();
|
|
|
+ BigDecimal totalAmount = unitPrice.multiply(BigDecimal.valueOf(candidateCount));
|
|
|
+ if (totalAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ throw new ServiceException("订单金额必须大于0");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Long> candidateRelationIds = new ArrayList<>(candidateCount);
|
|
|
+ for (PortalCheckOrderCreateBo.CandidateInfo candidateInfo : bo.getCandidates()) {
|
|
|
+ MainStudent student = matchStudent(candidateInfo);
|
|
|
+ MainBackCandidate relation = findOrCreateCandidateRelation(tenantId, student.getId());
|
|
|
+ candidateRelationIds.add(relation.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ String orderNo = generateOrderNo();
|
|
|
+
|
|
|
+ MainBackOrder backOrder = new MainBackOrder();
|
|
|
+ backOrder.setOrderNo(orderNo);
|
|
|
+ backOrder.setTenantId(tenantId);
|
|
|
+ backOrder.setCategoryId(categoryId);
|
|
|
+ backOrder.setCategoryName(categoryName);
|
|
|
+ backOrder.setTotalAmount(totalAmount);
|
|
|
+ backOrder.setClauseIds(clauseIdsJson);
|
|
|
+ backOrder.setClauseGroupKey(clauseGroupKey);
|
|
|
+ backOrder.setStatus("0");
|
|
|
+ baseMapper.insert(backOrder);
|
|
|
+
|
|
|
+ MainOrder mainOrder = new MainOrder();
|
|
|
+ mainOrder.setOrderNo(orderNo);
|
|
|
+ mainOrder.setOrderType(3);
|
|
|
+ mainOrder.setBuyerType(1);
|
|
|
+ mainOrder.setBuyerId(tenant.getId());
|
|
|
+ mainOrder.setBuyerName(tenant.getCompanyName());
|
|
|
+ mainOrder.setSellerId(1L);
|
|
|
+ mainOrder.setTotalAmount(totalAmount);
|
|
|
+ mainOrder.setPaidAmount(BigDecimal.ZERO);
|
|
|
+ mainOrder.setRefundAmount(BigDecimal.ZERO);
|
|
|
+ mainOrder.setOrderStatus(0);
|
|
|
+ mainOrder.setPayStatus(0);
|
|
|
+ mainOrder.setBusinessId(backOrder.getId());
|
|
|
+ mainOrder.setTenantId(tenantId);
|
|
|
+ mainOrderMapper.insert(mainOrder);
|
|
|
+
|
|
|
+ for (Long candidateRelationId : candidateRelationIds) {
|
|
|
+ MainBackRecord record = new MainBackRecord();
|
|
|
+ record.setOrderId(backOrder.getId());
|
|
|
+ record.setCandidateId(candidateRelationId);
|
|
|
+ record.setStatus("未完成");
|
|
|
+ recordMapper.insert(record);
|
|
|
+ }
|
|
|
+
|
|
|
+ MainBackOrderVo vo = MapstructUtils.convert(backOrder, MainBackOrderVo.class);
|
|
|
+ vo.setOrderId(mainOrder.getId());
|
|
|
+ vo.setOrderNo(mainOrder.getOrderNo());
|
|
|
+ vo.setCompanyId(tenant.getId());
|
|
|
+ vo.setCompanyName(tenant.getCompanyName());
|
|
|
+ vo.setTenantId(tenantId);
|
|
|
+ vo.setOrderType(bo.getOrderType());
|
|
|
+ vo.setClauseIds(clauseIdsJson);
|
|
|
+ vo.setUnitPrice(unitPrice);
|
|
|
+ vo.setTotalAmount(totalAmount);
|
|
|
+ vo.setOrderStatus(0);
|
|
|
+ vo.setMainOrderStatus(0);
|
|
|
+ vo.setCandidateCount((long) candidateCount);
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
private void fillPortalFields(MainBackOrderVo vo, MainOrder mainOrder) {
|
|
|
Long count = recordMapper.selectCount(
|
|
|
new LambdaQueryWrapper<MainBackRecord>()
|
|
|
@@ -146,6 +341,142 @@ public class MainBackOrderServiceImpl implements IMainBackOrderService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private MainStudent matchStudent(PortalCheckOrderCreateBo.CandidateInfo candidateInfo) {
|
|
|
+ MainStudent student = mainStudentMapper.selectOne(
|
|
|
+ Wrappers.<MainStudent>lambdaQuery()
|
|
|
+ .eq(MainStudent::getMobile, candidateInfo.getCandidatePhone())
|
|
|
+ .eq(MainStudent::getIdCardNumber, candidateInfo.getCandidateIdNumber())
|
|
|
+ .eq(MainStudent::getStatus, "0")
|
|
|
+ .last("limit 1")
|
|
|
+ );
|
|
|
+ if (student == null) {
|
|
|
+ throw new ServiceException(
|
|
|
+ "候选人【" + candidateInfo.getCandidateName() + "】未在学员库中找到,请先完成学员注册"
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return student;
|
|
|
+ }
|
|
|
+
|
|
|
+ private MainBackCandidate findOrCreateCandidateRelation(String tenantId, Long studentId) {
|
|
|
+ MainBackCandidate relation = mainBackCandidateMapper.selectOne(
|
|
|
+ Wrappers.<MainBackCandidate>lambdaQuery()
|
|
|
+ .eq(MainBackCandidate::getTenantId, tenantId)
|
|
|
+ .eq(MainBackCandidate::getStudentId, studentId)
|
|
|
+ .last("limit 1")
|
|
|
+ );
|
|
|
+ if (relation != null) {
|
|
|
+ return relation;
|
|
|
+ }
|
|
|
+
|
|
|
+ relation = new MainBackCandidate();
|
|
|
+ relation.setTenantId(tenantId);
|
|
|
+ relation.setStudentId(studentId);
|
|
|
+ relation.setSource("portal");
|
|
|
+ mainBackCandidateMapper.insert(relation);
|
|
|
+ return relation;
|
|
|
+ }
|
|
|
+
|
|
|
+ private BigDecimal defaultAmount(BigDecimal amount) {
|
|
|
+ return amount == null ? BigDecimal.ZERO : amount;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<MainBackOrderCandidateVo> buildPortalCandidates(Long backOrderId) {
|
|
|
+ List<MainBackRecord> records = recordMapper.selectList(
|
|
|
+ Wrappers.<MainBackRecord>lambdaQuery()
|
|
|
+ .eq(MainBackRecord::getOrderId, backOrderId)
|
|
|
+ .orderByAsc(MainBackRecord::getCreateTime)
|
|
|
+ );
|
|
|
+ if (records.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Long> candidateIds = records.stream()
|
|
|
+ .map(MainBackRecord::getCandidateId)
|
|
|
+ .distinct()
|
|
|
+ .toList();
|
|
|
+ Map<Long, MainBackCandidate> candidateMap = new HashMap<>();
|
|
|
+ for (MainBackCandidate candidate : mainBackCandidateMapper.selectBatchIds(candidateIds)) {
|
|
|
+ candidateMap.put(candidate.getId(), candidate);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Long> studentIds = candidateMap.values().stream()
|
|
|
+ .map(MainBackCandidate::getStudentId)
|
|
|
+ .filter(java.util.Objects::nonNull)
|
|
|
+ .distinct()
|
|
|
+ .toList();
|
|
|
+ Map<Long, MainStudent> studentMap = new HashMap<>();
|
|
|
+ for (MainStudent student : mainStudentMapper.selectBatchIds(studentIds)) {
|
|
|
+ studentMap.put(student.getId(), student);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<MainBackOrderCandidateVo> result = new ArrayList<>(records.size());
|
|
|
+ for (MainBackRecord record : records) {
|
|
|
+ MainBackCandidate candidate = candidateMap.get(record.getCandidateId());
|
|
|
+ MainStudent student = candidate == null ? null : studentMap.get(candidate.getStudentId());
|
|
|
+
|
|
|
+ MainBackOrderCandidateVo vo = new MainBackOrderCandidateVo();
|
|
|
+ vo.setId(record.getId());
|
|
|
+ vo.setOrderId(backOrderId);
|
|
|
+ vo.setStudentName(student == null ? null : student.getName());
|
|
|
+ vo.setCandidatePhone(student == null ? null : student.getMobile());
|
|
|
+ vo.setCandidateIdType("1");
|
|
|
+ vo.setCandidateIdNumber(student == null ? null : student.getIdCardNumber());
|
|
|
+ vo.setStatus(mapRecordStatus(record.getStatus()));
|
|
|
+ vo.setReportUrl(record.getReportUrl());
|
|
|
+ result.add(vo);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<MainBackClauseVo> buildPortalClauses(MainBackOrder backOrder) {
|
|
|
+ if (backOrder.getCategoryId() != null) {
|
|
|
+ return mainBackClauseMapper.selectVoList(
|
|
|
+ Wrappers.<MainBackClause>lambdaQuery()
|
|
|
+ .eq(MainBackClause::getCategoryId, backOrder.getCategoryId())
|
|
|
+ .eq(MainBackClause::getStatus, "1")
|
|
|
+ .orderByAsc(MainBackClause::getId)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(backOrder.getClauseIds())) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Long> clauseIds = JsonUtils.parseArray(backOrder.getClauseIds(), Long.class);
|
|
|
+ if (clauseIds == null || clauseIds.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<Long, MainBackClauseVo> clauseMap = mainBackClauseMapper.selectVoList(
|
|
|
+ Wrappers.<MainBackClause>lambdaQuery().in(MainBackClause::getId, clauseIds)
|
|
|
+ ).stream().collect(Collectors.toMap(MainBackClauseVo::getId, item -> item, (a, b) -> a));
|
|
|
+
|
|
|
+ List<MainBackClauseVo> result = new ArrayList<>();
|
|
|
+ for (Long clauseId : clauseIds) {
|
|
|
+ MainBackClauseVo clause = clauseMap.get(clauseId);
|
|
|
+ if (clause != null) {
|
|
|
+ result.add(clause);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String generateOrderNo() {
|
|
|
+ return "ORD" + cn.hutool.core.date.DateUtil.format(new Date(), "yyyyMMddHHmmss")
|
|
|
+ + cn.hutool.core.util.RandomUtil.randomNumbers(4);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Integer mapRecordStatus(String status) {
|
|
|
+ if (StringUtils.isBlank(status)) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return switch (status) {
|
|
|
+ case "进行中" -> 1;
|
|
|
+ case "已完成", "完成" -> 2;
|
|
|
+ case "已取消", "失败" -> 3;
|
|
|
+ default -> 0;
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
private Integer parseIntOrDefault(String value, int defaultValue) {
|
|
|
if (StringUtils.isBlank(value)) {
|
|
|
return defaultValue;
|