| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443 |
- package com.ylx.massage.service.impl;
- import java.math.BigDecimal;
- import java.text.SimpleDateFormat;
- import java.time.Duration;
- import java.time.LocalDate;
- import java.time.LocalDateTime;
- import java.time.ZoneId;
- import java.time.format.DateTimeFormatter;
- import java.util.*;
- import java.util.function.Function;
- import java.util.stream.Collectors;
- import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
- import com.baomidou.mybatisplus.core.toolkit.Wrappers;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.ylx.attendanceconfig.domain.AttendanceRule;
- import com.ylx.attendanceconfig.mapper.AttendanceRuleMapper;
- import com.ylx.common.core.domain.AjaxResult;
- import com.ylx.common.core.domain.model.LoginUser;
- import com.ylx.common.exception.ServiceException;
- import com.ylx.common.utils.DateUtils;
- import com.ylx.common.utils.StringUtils;
- import com.ylx.massage.controller.CityOperationApplicationController;
- import com.ylx.massage.domain.*;
- import com.ylx.massage.domain.dto.*;
- import com.ylx.massage.domain.vo.*;
- import com.ylx.massage.enums.*;
- import com.ylx.massage.mapper.*;
- import com.ylx.massage.domain.ContractRecord;
- import com.ylx.massage.domain.MaProject;
- import com.ylx.massage.domain.MaTeProject;
- import com.ylx.massage.domain.dto.MaProjectSaveDto;
- import com.ylx.massage.domain.dto.MaTechnicianAuditQueryDTO;
- import com.ylx.massage.domain.dto.MaTechnicianAuditSubmitDTO;
- import com.ylx.massage.domain.dto.MaTechnicianMerchantAddDTO;
- import com.ylx.massage.domain.dto.MaTechnicianMerchantQueryDTO;
- import com.ylx.massage.domain.dto.MassageMerchantRecommendDto;
- import com.ylx.massage.domain.vo.MaTechnicianAppAddVo;
- import com.ylx.massage.domain.vo.MaTechnicianAuditListVO;
- import com.ylx.massage.domain.vo.MaTechnicianCertificateVO;
- import com.ylx.massage.domain.vo.MaTechnicianMerchantDetailVO;
- import com.ylx.massage.domain.vo.MaTechnicianMerchantListVO;
- import com.ylx.massage.domain.vo.MerchantVo;
- import com.ylx.massage.mapper.ContractRecordMapper;
- import com.ylx.massage.mapper.MaProjectMapper;
- import com.ylx.massage.mapper.MaTeProjectMapper;
- import com.ylx.massage.service.TbFileService;
- import com.ylx.order.domain.TOrder;
- import com.ylx.order.mapper.TOrderMapper;
- import com.ylx.project.domain.Project;
- import com.ylx.project.mapper.ProjectMapper;
- import lombok.Data;
- import org.springframework.beans.BeanUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
- import org.springframework.stereotype.Service;
- import com.ylx.massage.service.IMaTechnicianService;
- import org.springframework.transaction.annotation.Transactional;
- import org.springframework.util.CollectionUtils;
- import org.springframework.web.multipart.MultipartFile;
- import javax.annotation.Resource;
- import static com.ylx.massage.enums.FileTypeEnum.*;
- /**
- * 技师Service业务层处理
- *
- * @author ylx
- * @date 2024-03-22
- */
- @Service
- public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaTechnician> implements IMaTechnicianService {
- private static final int SERVICE_STATE_AVAILABLE = 1;
- private static final int POST_STATE_OFFLINE = 0;
- private static final int ENABLED = 1;
- private static final int DEFAULT_AGE = 18;
- private static final int AUDIT_APPROVED = 2;
- private static final int AUDIT_WAIT_ENTER = 0;
- private static final int AUDIT_WAIT_REVIEW = 1;
- private static final int AUDIT_REJECTED = 3;
- private static final int AUDIT_REMARK_MAX_LENGTH = 500;
- private static final int NS_STATUS_NOT_ON_DUTY = -1;
- private static final int DEFAULT_STAT_VALUE = 0;
- private static final Integer NOT_DELETED = 0;
- private static final String MERCHANT_STATUS_NORMAL = "0";
- private static final String PASSWORD = "123456";
- @Resource
- private MaTechnicianMapper maTechnicianMapper;
- @Resource
- private MaTeProjectMapper maTeProjectMapper;
- @Resource
- private MaProjectMapper maProjectMapper;
- @Autowired
- private ProjectMapper projectMapper;
- @Autowired
- private TbFileService fileService;
- @Resource
- private TFareFreeRuleMapper tFareFreeRuleMapper;
- @Resource
- private ContractRecordMapper contractRecordMapper;
- @Resource
- private MerchantDailyAttendanceMapper merchantDailyAttendanceMapper;
- @Resource
- private AttendanceRuleMapper attendanceRuleMapper;
- @Resource
- private TAddressMapper addressMapper;
- @Resource
- private MerchantApplyFileMapper merchantApplyFileMapper;
- @Resource
- private TOrderMapper orderMapper;
- @Autowired
- private IMaTechnicianService maTechnicianService;
- @Resource
- private CityOperationApplicationMapper cityOperationApplicationMapper;
- /**
- * 商户入驻申请注册
- *
- * @param req 申请参数
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void apply(MaTechnicianAppAddVo req) {
- // 初始化加密工具
- BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
- String phone = req.getTePhone();
- //商户入住前置条件校验
- getMaTechnician(req, phone);
- MaTechnician maTechnician = new MaTechnician();
- BeanUtils.copyProperties(req, maTechnician);
- //技师类型默认为真实商户
- maTechnician.setTechType(0);
- maTechnician.setCreateBy("admin");
- maTechnician.setAuditStatus(AUDIT_WAIT_ENTER);
- maTechnician.setTePassword(encoder.encode(PASSWORD));
- LambdaQueryWrapper<MaTechnician> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(MaTechnician::getCOpenid, req.getCOpenid());
- MaTechnician maTechnician1 = maTechnicianMapper.selectOne(queryWrapper);
- if (maTechnician1 == null) {
- throw new RuntimeException("商户不存在");
- }
- LambdaUpdateWrapper<MaTechnician> updateWrapper = new LambdaUpdateWrapper<>();
- updateWrapper.eq(MaTechnician::getId, maTechnician1.getId());
- maTechnicianMapper.update(maTechnician, updateWrapper);
- CityOperationApplication cityOperationApplication = new CityOperationApplication();
- cityOperationApplication.setMerchantId(maTechnician1.getId());
- cityOperationApplication.setOperationCenterId(req.getOperationCenterId());
- cityOperationApplication.setProvinceCode(req.getProvinceCode());
- cityOperationApplication.setProvinceName(req.getProvinceName());
- cityOperationApplication.setCityCode(req.getCityCode());
- cityOperationApplication.setCityName(req.getCityName());
- cityOperationApplication.setDistrictCode(req.getDistrictCode());
- cityOperationApplication.setDistrictName(req.getDistrictName());
- cityOperationApplication.setOperationCenterName(req.getOperationCenterName());
- cityOperationApplication.setCreateBy(maTechnician1.getId().toString());
- cityOperationApplication.setUpdateBy(maTechnician1.getId().toString());
- cityOperationApplicationMapper.insert(cityOperationApplication);
- }
- /**
- * 商户入驻申请文件上传
- *
- * @param req
- */
- @Override
- public void applyFile(MerchantApplyFileRequestDto req) {
- if (req == null || req.getReq().isEmpty()) {
- }
- for (MerchantApplyFileDto re : req.getReq()) {
- LambdaQueryWrapper<MerchantApplyFile> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(MerchantApplyFile::getMerchantId, re.getMerchantId());
- queryWrapper.eq(MerchantApplyFile::getFileType, re.getFileType());
- MerchantApplyFile merchantApplyFile = merchantApplyFileMapper.selectOne(queryWrapper);
- if (merchantApplyFile != null) {
- // 删除原有文件
- merchantApplyFileMapper.deleteById(merchantApplyFile);
- } else {
- //插入文件信息
- MerchantApplyFile maTechnician = new MerchantApplyFile();
- BeanUtils.copyProperties(re, maTechnician);
- maTechnician.setCreateBy(re.getMerchantId().toString());
- maTechnician.setUpdateBy(re.getMerchantId().toString());
- merchantApplyFileMapper.insert(maTechnician);
- }
- }
- LambdaUpdateWrapper<MaTechnician> updateWrapper = new LambdaUpdateWrapper<>();
- updateWrapper.eq(MaTechnician::getId, req.getTechnician().getId());
- updateWrapper.set(MaTechnician::getTeNickName, req.getTechnician().getTeNickName());
- updateWrapper.set(MaTechnician::getTeBrief, req.getTechnician().getTeBrief());
- maTechnicianService.update(updateWrapper);
- }
- /**
- * 商户入住前置条件校验
- *
- * @param req
- * @param phone
- */
- private void getMaTechnician(MaTechnicianAppAddVo req, String phone) {
- // 1. 判断当前用户是否已入驻
- MaTechnician userProfile = getMaTechnician(req);
- if (userProfile != null) {
- throw new RuntimeException("当前用户已入驻,请勿重复提交");
- }
- // 2. 判断手机号是否已存在
- LambdaQueryWrapper<MaTechnician> queryPhoneWrapper = new LambdaQueryWrapper<>();
- queryPhoneWrapper.eq(MaTechnician::getTePhone, phone);
- queryPhoneWrapper.eq(MaTechnician::getIsDelete, 0);
- MaTechnician maTechnicianPhone = maTechnicianMapper.selectOne(queryPhoneWrapper);
- if (maTechnicianPhone != null) {
- throw new RuntimeException("手机号已存在,请更换手机号");
- }
- //3、判断手机号是否已绑定其他用户
- LambdaQueryWrapper<MaTechnician> queryTePhoneWrapper = new LambdaQueryWrapper<>();
- queryTePhoneWrapper.eq(MaTechnician::getTePhone, phone);
- queryTePhoneWrapper.eq(MaTechnician::getIsDelete, 0);
- queryTePhoneWrapper.eq(MaTechnician::getAuditStatus, 2);
- MaTechnician maTechnicianTePhone = maTechnicianMapper.selectOne(queryTePhoneWrapper);
- if (maTechnicianTePhone != null) {
- throw new RuntimeException("手机号已被其他用户绑定,请更换手机号");
- }
- }
- /**
- * 判断当前用户是否已入驻
- *
- * @return
- */
- private MaTechnician getMaTechnician(MaTechnicianAppAddVo req) {
- LambdaQueryWrapper<MaTechnician> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(MaTechnician::getTePhone, req.getTePhone());
- queryWrapper.eq(MaTechnician::getIsDelete, 0);
- queryWrapper.eq(MaTechnician::getAuditStatus, 2);
- //queryWrapper.eq(MaTechnician::getOpenService, req.getOpenService());
- queryWrapper.eq(MaTechnician::getServiceTag, req.getServiceTag());
- MaTechnician userProfile = maTechnicianMapper.selectOne(queryWrapper);
- return userProfile;
- }
- /**
- * 查询商户服务项目列表
- *
- * @param userId 商户id
- * @param auditStatus 审核状态
- * @return 技师列表
- */
- @Override
- public List<MaProject> selectMaTechnicianListBy(String userId, String auditStatus) {
- LambdaQueryWrapper<MaProject> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(MaProject::getMerchantId, userId);
- queryWrapper.eq(MaProject::getAuditStatus, auditStatus);
- return maProjectMapper.selectList(queryWrapper);
- }
- /**
- * 查询服务分类项目列表
- *
- * @param typeId 技师类型
- * @return 技师列表
- */
- @Override
- public List<Project> selectTechnicianListBy(String typeId) {
- LambdaQueryWrapper<Project> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(Project::getType, typeId);
- return projectMapper.selectList(queryWrapper);
- }
- /**
- * 查询技师
- *
- * @param id 技师主键
- * @return 技师
- */
- @Override
- public MaTechnician selectMaTechnicianById(Long id) {
- return maTechnicianMapper.selectMaTechnicianById(id);
- }
- /**
- * 查询技师列表
- *
- * @param maTechnician 技师
- * @return 技师
- */
- @Override
- public List<MaTechnician> selectMaTechnicianList(MaTechnician maTechnician) {
- return maTechnicianMapper.selectMaTechnicianList(maTechnician);
- }
- /**
- * 新增技师
- *
- * @param maTechnicianAppAddVo 技师
- * @return 结果
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public int insertMaTechnician(MaTechnicianAppAddVo maTechnicianAppAddVo) {
- MaTechnician maTechnician = new MaTechnician();
- BeanUtils.copyProperties(maTechnicianAppAddVo, maTechnician);
- int rows = maTechnicianMapper.insertMaTechnician(maTechnician);
- if (maTechnicianAppAddVo.getProjectIds() != null && !maTechnicianAppAddVo.getProjectIds().isEmpty()) {
- insertProjectRelations(maTechnician.getId(), new LinkedHashSet<>(maTechnicianAppAddVo.getProjectIds()));
- }
- return rows;
- }
- /**
- * 后台新增商户
- *
- * @param dto 新增商户参数
- * @param loginUser 当前登录用户
- * @return 结果
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public int insertMerchant(MaTechnicianMerchantAddDTO dto, LoginUser loginUser) {
- MerchantProjectSelection selection = checkMerchantAddParam(dto);
- String userName = loginUser.getUser().getUserName();
- MaTechnician maTechnician = new MaTechnician();
- maTechnician.setTeName(dto.getTeName().trim());
- maTechnician.setTeNickName(dto.getTeNickName().trim());
- maTechnician.setTeSex(dto.getTeSex());
- maTechnician.setTePhone(dto.getTePhone().trim());
- maTechnician.setOpenService(joinIds(selection.getCategoryIds()));
- maTechnician.setTeProject(joinProjectTitles(selection.getProjectIds(), selection.getProjectMap()));
- maTechnician.setTechType(dto.getTechType());
- maTechnician.setIsRecommend(normalizeSwitchValue(dto.getIsRecommend(), "是否推荐"));
- maTechnician.setServiceState(SERVICE_STATE_AVAILABLE);
- maTechnician.setPostState(POST_STATE_OFFLINE);
- maTechnician.setTeIsEnable(ENABLED);
- //上岗状态:默认-1 未上岗
- maTechnician.setNStatus2(NS_STATUS_NOT_ON_DUTY);
- maTechnician.setMerchantStatus(MERCHANT_STATUS_NORMAL);
- //审核状态
- maTechnician.setAuditStatus(AUDIT_APPROVED);
- maTechnician.setTeAddress("");
- maTechnician.setNStar(DEFAULT_STAT_VALUE);
- maTechnician.setNNum(DEFAULT_STAT_VALUE);
- maTechnician.setCreateBy(userName);
- maTechnician.setUpdateBy(userName);
- maTechnician.setCreateTime(DateUtils.getNowDate());
- maTechnician.setUpdateTime(DateUtils.getNowDate());
- int rows = maTechnicianMapper.insert(maTechnician);
- if (rows <= 0) {
- throw new ServiceException("新增商户失败");
- }
- insertProjectRelations(maTechnician.getId(), selection.getProjectIds());
- return rows;
- }
- /**
- * 后台编辑商户
- *
- * @param id 商户ID
- * @param dto 编辑商户参数
- * @param loginUser 当前登录用户
- * @return 结果
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public int updateMerchant(Integer id, MaTechnicianMerchantAddDTO dto, LoginUser loginUser) {
- if (id == null) {
- throw new ServiceException("商户ID不能为空");
- }
- MaTechnician existsMerchant = maTechnicianMapper.selectMerchantById(id.intValue());
- if (existsMerchant == null || !NOT_DELETED.equals(existsMerchant.getIsDelete())) {
- throw new ServiceException("商户不存在或已删除");
- }
- MerchantProjectSelection selection = checkMerchantAddParam(dto);
- String userName = loginUser.getUser().getUserName();
- MaTechnician maTechnician = new MaTechnician();
- maTechnician.setId(id);
- maTechnician.setTeName(dto.getTeName().trim());
- maTechnician.setTeNickName(dto.getTeNickName().trim());
- maTechnician.setTeSex(dto.getTeSex());
- maTechnician.setTePhone(dto.getTePhone().trim());
- maTechnician.setOpenService(joinIds(selection.getCategoryIds()));
- maTechnician.setTeProject(joinProjectTitles(selection.getProjectIds(), selection.getProjectMap()));
- maTechnician.setTechType(dto.getTechType());
- maTechnician.setIsRecommend(normalizeSwitchValue(dto.getIsRecommend(), "是否推荐"));
- maTechnician.setUpdateBy(userName);
- maTechnician.setUpdateTime(DateUtils.getNowDate());
- int rows = maTechnicianMapper.updateMerchantById(maTechnician);
- if (rows <= 0) {
- throw new ServiceException("编辑商户失败");
- }
- replaceProjectRelations(id, selection.getProjectIds());
- return rows;
- }
- /**
- * 后台上传商户合同文件
- *
- * @param id 商户ID
- * @param
- * @param loginUser 当前登录用户
- * @return 上传结果
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public Integer uploadMerchantContract(Integer id, Map<String, Object> map, LoginUser loginUser) {
- if (id == null) {
- throw new ServiceException("商户ID不能为空");
- }
- MaTechnician existsMerchant = maTechnicianMapper.selectMerchantById(id);
- if (existsMerchant == null || !NOT_DELETED.equals(existsMerchant.getIsDelete())) {
- throw new ServiceException("商户不存在或已删除");
- }
- // 合同的名称
- String contractName = String.valueOf(map.get("contractName"));
- // 合同文件的URL
- String url = String.valueOf(map.get("url"));
- if (StringUtils.isBlank(url)) {
- throw new ServiceException("合同文件上传失败,未返回文件地址");
- }
- ContractRecord contractRecord = new ContractRecord();
- contractRecord.setMerchantId(id);
- contractRecord.setContractName(contractName);
- contractRecord.setFileUrl(url);
- contractRecord.setSignTime(DateUtils.getNowDate());
- contractRecord.setSignerName(existsMerchant.getTeName());
- contractRecord.setCreateTime(DateUtils.getNowDate());
- int rows = contractRecordMapper.insert(contractRecord);
- if (rows <= 0) {
- throw new ServiceException("保存合同记录失败");
- }
- return rows;
- }
- /**
- * 商户入驻审核。
- *
- * @param id 商户ID
- * @param dto 审核提交参数
- * @param loginUser 当前登录用户
- * @return 结果
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public int submitMerchantAudit(Integer id, MaTechnicianAuditSubmitDTO dto, LoginUser loginUser) {
- if (id == null) {
- throw new ServiceException("商户ID不能为空");
- }
- if (dto == null) {
- throw new ServiceException("审核参数不能为空");
- }
- checkEnumValue(dto.getAuditStatus(), "审核意见", AUDIT_APPROVED, AUDIT_REJECTED);
- String auditRemark = dto.getAuditRemark() == null ? "" : dto.getAuditRemark().trim();
- if (dto.getAuditStatus() == AUDIT_REJECTED && StringUtils.isBlank(auditRemark)) {
- throw new ServiceException("审核驳回时审核备注不能为空");
- }
- if (auditRemark.length() > AUDIT_REMARK_MAX_LENGTH) {
- throw new ServiceException("审核备注长度不能超过" + AUDIT_REMARK_MAX_LENGTH + "个字符");
- }
- MaTechnician existsMerchant = maTechnicianMapper.selectMerchantById(id);
- if (existsMerchant == null || !NOT_DELETED.equals(existsMerchant.getIsDelete())) {
- throw new ServiceException("商户不存在或已删除");
- }
- Integer currentAuditStatus = existsMerchant.getAuditStatus();
- if (currentAuditStatus == null || (currentAuditStatus != AUDIT_WAIT_ENTER)) {
- throw new ServiceException("当前商户待入驻已审核,不用重复审核");
- }
- MaTechnician maTechnician = new MaTechnician();
- maTechnician.setId(id);
- // 修改为1:待审核
- maTechnician.setAuditStatus(1);
- maTechnician.setAuditRemark(auditRemark);
- maTechnician.setApproveTime(DateUtils.getNowDate());
- if (loginUser != null && loginUser.getUser() != null) {
- maTechnician.setUpdateBy(loginUser.getUser().getUserName());
- }
- maTechnician.setUpdateTime(DateUtils.getNowDate());
- int rows = maTechnicianMapper.submitMerchantAuditById(maTechnician);
- if (rows <= 0) {
- throw new ServiceException("提交商户审核失败");
- }
- return rows;
- }
- /**
- * 后台查询商户证照
- *
- * @param id 商户ID
- * @return 商户证照
- */
- @Override
- public MaTechnicianCertificateVO selectMerchantCertificate(Integer id) {
- if (id == null) {
- throw new ServiceException("商户ID不能为空");
- }
- LambdaQueryWrapper<MerchantApplyFile> queryWrapper = Wrappers.lambdaQuery();
- queryWrapper.eq(MerchantApplyFile::getMerchantId, id);
- List<MerchantApplyFile> merchantApplyFiles = merchantApplyFileMapper.selectList(queryWrapper);
- if (merchantApplyFiles == null) {
- throw new ServiceException("商户不存在或已删除");
- }
- MaTechnicianCertificateVO certificate = new MaTechnicianCertificateVO();
- certificate.setMerchantId(merchantApplyFiles.get(0).getMerchantId());
- merchantApplyFiles.forEach(merchant -> {
- certificate.setAvatar(typeFIleUrl(merchant, PORTRAIT.getCode()));
- certificate.setLifePhotos(typeFIleUrl(merchant, LIFE_PHOTO.getCode()));
- certificate.setIdCardFrout(typeFIleUrl(merchant, ID_CARD_FRONT.getCode()));
- certificate.setIdCardBack(typeFIleUrl(merchant, ID_CARD_BACK.getCode()));
- certificate.setIdCardHandheld(typeFIleUrl(merchant, ID_CARD_HANDHELD.getCode()));
- certificate.setHealthCertificate(typeFIleUrl(merchant, HEALTH_CERT.getCode()));
- certificate.setQualificationCertificate(typeFIleUrl(merchant, QUALIFICATION_CERT.getCode()));
- certificate.setNoCrimeRecord(typeFIleUrl(merchant, NO_CRIME_RECORD.getCode()));
- certificate.setCommitmentPdf(typeFIleUrl(merchant, COMMITMENT_LETTER.getCode()));
- certificate.setCommitmentVideo(typeFIleUrl(merchant, COMMITMENT_VIDEO.getCode()));
- certificate.setCommitmentAudio(typeFIleUrl(merchant, COMMITMENT_AUDIO.getCode()));
- });
- return certificate;
- }
- private String typeFIleUrl(MerchantApplyFile merchant, String type) {
- LambdaQueryWrapper<MerchantApplyFile> queryWrapper1 = Wrappers.lambdaQuery();
- queryWrapper1.eq(MerchantApplyFile::getMerchantId, merchant.getMerchantId());
- queryWrapper1.eq(MerchantApplyFile::getFileType, type);
- MerchantApplyFile merchantApplyFiles = merchantApplyFileMapper.selectOne(queryWrapper1);
- if (merchantApplyFiles == null) {
- return null;
- }
- return merchantApplyFiles.getFileUrl();
- }
- /**
- * 全量替换商户与服务项目关联关系。
- *
- * @param technicianId 商户ID
- * @param projectIds 服务项目ID集合
- */
- private void replaceProjectRelations(Integer technicianId, Set<Integer> projectIds) {
- if (technicianId == null) {
- throw new ServiceException("商户ID不能为空");
- }
- maTeProjectMapper.deleteByTechnicianId(technicianId);
- for (Integer projectId : projectIds) {
- MaTeProject relation = new MaTeProject();
- relation.setTeId(technicianId);
- relation.setProjectId(projectId);
- int rows = maTeProjectMapper.insert(relation);
- if (rows <= 0) {
- throw new ServiceException("编辑商户服务项目失败");
- }
- }
- }
- /**
- * 后台查询商户入驻审核列表
- *
- * @param page 分页参数
- * @param dto 查询条件
- * @return 商户入驻审核分页列表
- */
- @Override
- public Page<MaTechnicianAuditListVO> selectMerchantAuditList(Page<MaTechnicianAuditListVO> page, MaTechnicianAuditQueryDTO dto) {
- if (dto != null && dto.getAuditStatus() != null) {
- checkEnumValue(dto.getAuditStatus(), "审核状态", 0, 1, 2, 3);
- }
- Page<MaTechnicianAuditListVO> pageParam = page == null ? new Page<>(1, 10) : page;
- return maTechnicianMapper.selectMerchantAuditList(pageParam, dto);
- }
- /**
- * 后台查询商户列表
- *
- * @param page 分页参数
- * @param dto 查询条件
- * @return 商户分页列表
- */
- @Override
- public Page<MaTechnicianMerchantListVO> selectMerchantList(Page<MaTechnicianMerchantListVO> page,
- MaTechnicianMerchantQueryDTO dto) {
- Page<MaTechnicianMerchantListVO> pageParam = page == null ? new Page<>(1, 10) : page;
- return maTechnicianMapper.selectMerchantList(pageParam, dto);
- }
- /**
- * 后台查询商户详情
- *
- * @param id 商户ID
- * @return 商户详情
- */
- @Override
- public MaTechnicianMerchantDetailVO selectMerchantDetail(Long id) {
- if (id == null) {
- throw new ServiceException("商户ID不能为空");
- }
- MaTechnicianMerchantDetailVO detail = maTechnicianMapper.selectMerchantDetailById(id);
- if (detail == null) {
- throw new ServiceException("商户不存在或已删除");
- }
- return detail;
- }
- /**
- * 修改技师
- *
- * @param maTechnicianAppAddVo
- * @return 结果
- */
- @Override
- public int updateMaTechnician(MaTechnicianAppAddVo maTechnicianAppAddVo) {
- MaTechnician maTechnician = new MaTechnician();
- BeanUtils.copyProperties(maTechnicianAppAddVo, maTechnician);
- return maTechnicianMapper.updateMaTechnician(maTechnician);
- }
- /**
- * 批量删除技师
- *
- * @param ids 需要删除的技师主键
- * @return 结果
- */
- @Override
- public int deleteMaTechnicianByIds(Long[] ids) {
- return maTechnicianMapper.deleteMaTechnicianByIds(ids);
- }
- /**
- * 删除技师信息
- *
- * @param id 技师主键
- * @return 结果
- */
- @Override
- public int deleteMaTechnicianById(Long id) {
- return maTechnicianMapper.deleteMaTechnicianById(id);
- }
- /**
- * 首页选中的城市是否有开通服务
- *
- * @param areaCode
- * @return
- */
- @Override
- public Boolean isHasMerchantCity(String areaCode) {
- return maTechnicianMapper.isHasMerchantCity(areaCode);
- }
- /**
- * 首页按摩推荐
- *
- * @param dto
- * @return
- */
- @Override
- public List<MerchantVo> getMerchantRecommend(MassageMerchantRecommendDto dto) {
- return maTechnicianMapper.getMerchantRecommend(dto);
- }
- private MerchantProjectSelection checkMerchantAddParam(MaTechnicianMerchantAddDTO dto) {
- if (dto == null) {
- throw new ServiceException("商户参数不能为空");
- }
- checkRequiredText(dto.getTeName(), "姓名", 10);
- checkRequiredText(dto.getTeNickName(), "昵称", 10);
- checkRequiredText(dto.getTePhone(), "电话", 11);
- checkEnumValue(dto.getTeSex(), "性别", 0, 1);
- Set<Integer> categoryIds = checkOpenServiceIds(dto.getOpenService());
- checkEnumValue(dto.getTechType(), "商户类型", 0, 1);
- if (dto.getIsRecommend() != null) {
- checkEnumValue(dto.getIsRecommend(), "是否推荐", 0, 1);
- }
- return checkProjectIds(dto.getProjectIds(), categoryIds);
- }
- private void checkRequiredText(String value, String fieldName, int maxLength) {
- if (StringUtils.isBlank(value)) {
- throw new ServiceException(fieldName + "不能为空");
- }
- if (value.trim().length() > maxLength) {
- throw new ServiceException(fieldName + "长度不能超过" + maxLength + "个字符");
- }
- }
- private void checkEnumValue(Integer value, String fieldName, int... allowedValues) {
- if (value == null) {
- throw new ServiceException(fieldName + "不能为空");
- }
- for (int allowedValue : allowedValues) {
- if (value == allowedValue) {
- return;
- }
- }
- throw new ServiceException(fieldName + "值不正确");
- }
- private Integer normalizeSwitchValue(Integer value, String fieldName) {
- if (value == null) {
- return 0;
- }
- checkEnumValue(value, fieldName, 0, 1);
- return value;
- }
- /**
- * 校验服务项目ID集合
- *
- * @param projectIds 服务项目ID集合
- * @param categoryIds 服务类目ID集合
- * @return 有效服务项目ID集合
- */
- private MerchantProjectSelection checkProjectIds(List<Integer> projectIds, Set<Integer> categoryIds) {
- if (projectIds == null || projectIds.isEmpty()) {
- throw new ServiceException("服务项目不能为空");
- }
- Set<Integer> distinctProjectIds = new LinkedHashSet<>();
- for (Integer projectId : projectIds) {
- if (projectId == null) {
- throw new ServiceException("服务项目ID不能为空");
- }
- distinctProjectIds.add(projectId);
- }
- List<Project> projects = projectMapper.selectList(new LambdaQueryWrapper<Project>()
- .in(Project::getId, distinctProjectIds)
- .eq(Project::getIsDelete, 0));
- if (projects.size() != distinctProjectIds.size()) {
- throw new ServiceException("服务项目不存在或已删除");
- }
- Map<Integer, Project> projectMap = projects.stream()
- .collect(Collectors.toMap(project -> project.getId(), Function.identity(), (left, right) -> left));
- Set<Integer> projectCategoryIds = new LinkedHashSet<>();
- for (Integer projectId : distinctProjectIds) {
- Project project = projectMap.get(projectId);
- if (project == null || project.getCategoryId() == null) {
- throw new ServiceException("服务项目类目不能为空");
- }
- if (!categoryIds.contains(project.getCategoryId())) {
- throw new ServiceException("服务项目不属于所选服务类目");
- }
- projectCategoryIds.add(project.getCategoryId());
- }
- if (!projectCategoryIds.containsAll(categoryIds)) {
- throw new ServiceException("每个服务类目至少选择一个服务项目");
- }
- return new MerchantProjectSelection(categoryIds, distinctProjectIds, projectMap);
- }
- /**
- * 校验服务类目ID集合
- *
- * @param openService 服务类目ID集合
- * @return 去重后的服务类目ID集合
- */
- private Set<Integer> checkOpenServiceIds(List<Integer> openService) {
- if (openService == null || openService.isEmpty()) {
- throw new ServiceException("服务类目不能为空");
- }
- Set<Integer> categoryIds = new LinkedHashSet<>();
- for (Integer categoryId : openService) {
- if (categoryId == null) {
- throw new ServiceException("服务类目ID不能为空");
- }
- categoryIds.add(categoryId);
- }
- return categoryIds;
- }
- private String joinIds(Set<Integer> ids) {
- return ids.stream()
- .map(String::valueOf)
- .collect(Collectors.joining(","));
- }
- private String joinProjectTitles(Set<Integer> projectIds, Map<Integer, Project> projectMap) {
- return projectIds.stream()
- .map(projectMap::get)
- .map(Project::getTitle)
- .filter(StringUtils::isNotBlank)
- .collect(Collectors.joining(","));
- }
- /**
- * 新增商户与服务项目关联关系
- *
- * @param technicianId
- * @param projectIds
- */
- private void insertProjectRelations(Integer technicianId, Set<Integer> projectIds) {
- if (technicianId == null) {
- throw new ServiceException("商户ID不能为空");
- }
- List<MaTeProject> relations = new ArrayList<>();
- for (Integer projectId : projectIds) {
- MaTeProject relation = new MaTeProject();
- relation.setTeId(technicianId);
- relation.setProjectId(projectId);
- relations.add(relation);
- }
- int rows = maTeProjectMapper.insertBatch(relations);
- if (rows != relations.size()) {
- throw new ServiceException("新增商户服务项目失败");
- }
- }
- private static class MerchantProjectSelection {
- private final Set<Integer> categoryIds;
- private final Set<Integer> projectIds;
- private final Map<Integer, Project> projectMap;
- private MerchantProjectSelection(Set<Integer> categoryIds, Set<Integer> projectIds, Map<Integer, Project> projectMap) {
- this.categoryIds = categoryIds;
- this.projectIds = projectIds;
- this.projectMap = projectMap;
- }
- private Set<Integer> getCategoryIds() {
- return categoryIds;
- }
- private Set<Integer> getProjectIds() {
- return projectIds;
- }
- private Map<Integer, Project> getProjectMap() {
- return projectMap;
- }
- }
- /**
- * 获取未申请技能列表
- *
- * @param userId
- * @param typeId
- * @return
- */
- @Override
- public List<Project> getNotApplyList(String userId, String typeId) {
- LambdaQueryWrapper<MaProject> query = new LambdaQueryWrapper<>();
- query.eq(MaProject::getMerchantId, userId);
- query.eq(MaProject::getServiceTag, typeId);
- List<MaProject> maProjectList = maProjectMapper.selectList(query);
- // 获取已申请技能ID集合
- List<Long> projectIdList = maProjectList.stream().map(MaProject::getProjectId).collect(Collectors.toList());
- if (projectIdList.size() == 0) {
- LambdaQueryWrapper<Project> query1 = new LambdaQueryWrapper<>();
- query1.eq(Project::getType, typeId);
- return projectMapper.selectList(query1);
- }
- LambdaQueryWrapper<Project> query2 = new LambdaQueryWrapper<>();
- query2.eq(Project::getType, typeId);
- query2.notIn(Project::getId, projectIdList);
- return projectMapper.selectList(query2);
- }
- /**
- * 申请开通新服务
- *
- * @param dto
- * @return
- */
- @Transactional(rollbackFor = Exception.class)
- public int applyForService(MaProjectSaveDto dto) {
- if (Objects.isNull(dto)) {
- return 0;
- }
- if (dto.getProjectIdList().size() > 0) {
- // 插入商户技能
- extracted(dto);
- } else {
- return 0;
- }
- return 1;
- }
- /**
- * 商户入住信息
- *
- * @param userId
- * @return
- */
- @Override
- public MerchantAuditFile getTechnicianList(Long userId) {
- LambdaQueryWrapper<MaTechnician> query = new LambdaQueryWrapper<>();
- query.eq(MaTechnician::getId, userId);
- MaTechnician merchant = maTechnicianMapper.selectOne(query);
- LambdaQueryWrapper<MerchantApplyFile> query1 = new LambdaQueryWrapper<>();
- query1.eq(MerchantApplyFile::getMerchantId, userId);
- List<MerchantApplyFile> merchantApplyFile = merchantApplyFileMapper.selectList(query1);
- MerchantAuditFile merchantAuditFile = new MerchantAuditFile();
- merchantAuditFile.setMerchant(merchant);
- merchantAuditFile.setMerchantAuditFile(merchantApplyFile);
- return merchantAuditFile;
- }
- /**
- * 查询商户合同记录信息
- *
- * @param userId
- * @return
- */
- @Override
- public List<ContractRecord> getContractRecords(Long userId){
- LambdaQueryWrapper<ContractRecord> query = new LambdaQueryWrapper<>();
- query.eq(ContractRecord::getMerchantId, userId);
- List<ContractRecord> contractRecordList = contractRecordMapper.selectList(query);
- if(contractRecordList.size() == 0) {
- return new ArrayList<>();
- }else {
- Set<String> seen = new HashSet<>();
- contractRecordList = contractRecordList.stream()
- .filter(record -> record.getContractName() != null && seen.add(record.getContractName()))
- .collect(Collectors.toList());
- }
- return contractRecordList;
- }
- private void extracted(MaProjectSaveDto dto) {
- LambdaQueryWrapper<Project> query = new LambdaQueryWrapper<>();
- query.in(Project::getId, dto.getProjectIdList());
- List<Project> projectList = projectMapper.selectList(query);
- for (Project project : projectList) {
- MaProject maProject = new MaProject();
- maProject.setProjectId(project.getId().longValue());
- maProject.setProjectName(project.getTitle());
- maProject.setProjectDescribe(project.getDetail());
- maProject.setProjectDuration(project.getStandardDuration());
- maProject.setProjectOriginalPrice(project.getPrice());
- maProject.setProjectMaxPrice(project.getPriceMax());
- maProject.setProjectLowestPrice(project.getPriceMin());
- maProject.setCreateBy(dto.getUserId());
- maProject.setUpdateBy(dto.getUserId());
- maProject.setMerchantId(dto.getUserId());
- maProject.setApplyTime(DateUtils.getNowDate());
- maProject.setMerchantPhone(dto.getMerchantPhone());
- maProjectMapper.insert(maProject);
- }
- }
- /**
- * 状态切换
- *
- * @param userId
- * @param forceConfirm
- * @return
- */
- @Override
- public Result switchToOffline(Long userId, Boolean forceConfirm) {
- MaTechnician technician = getTechnician(userId);
- // 1. 基础权限校验 (对应流程图左下角)
- if (!hasActiveSkills(userId)) {
- throw new RuntimeException("请先申请开通技能");
- }
- if (!hasHomeAddress(userId)) {
- // 这里通常会触发前端弹窗要求添加地址,或者直接阻断
- throw new RuntimeException("请完善家庭地址");
- }
- if (ProjectCategoryEnum.MASSAGE.getCode().equals(technician.getServiceTag())) {
- // 2. 状态判断逻辑 (对应流程图中间的菱形判断)
- // 只有处于“在线接单”状态才需要检查疲劳度/时长限制
- if (TechnicianStatusEnum.ONLINE.getCode().equals(technician.getPostState())) {
- if (technician.getServiceState().equals(JsStatusEnum.JS_SERVICE.getCode())) {
- throw new ServiceException("您有服务中的订单,不能下岗");
- }
- // 获取今日的商户考勤记录
- MerchantDailyAttendance attendance = getTodayAttendance(userId);
- long minutesOnline = 0;
- if (attendance != null && attendance.getAttendanceStartTime() != null) {
- LocalDateTime localDateTime = attendance.getAttendanceStartTime().toInstant()
- .atZone(ZoneId.systemDefault())
- .toLocalDateTime();
- //计算截止现在的时长,单位为分钟
- minutesOnline = Duration.between(localDateTime, LocalDateTime.now()).toMinutes();
- // 计算今日的累加在线时长
- minutesOnline = minutesOnline + getWorkDuration(userId);
- }
- AttendanceRule rule = getAttendanceRule();
- if (rule != null) {
- // 将小时转换为分钟进行比较
- BigDecimal minutes = rule.getBasicWorkHours().multiply(new BigDecimal(60));
- // 2. 精确转换成 long(无小数、无溢出才成功)
- long requiredMinutes = minutes.longValueExact();
- // 判断是否超过了平台规定的在线时间 (X小时)
- if (minutesOnline < requiredMinutes) {
- // 情况 A: 超时了,且用户还没有点击“确认下线”(forceConfirm=false)
- if (!forceConfirm) {
- // 返回特定错误码或数据结构,告诉前端弹出“我在想想/确认下线”的模态框
- return Result.ok("平台对您的在线时间做了约定,每日在线需满足"
- + (requiredMinutes / 60) + "小时,距离您下线时间还剩余" + ((requiredMinutes / 60) - minutesOnline) + "小时"
- + "不满足在线时间将收到平台处罚,是否确认下线?");
- }
- // 情况 B: 超时了,但用户已经点击了“确认下线”,允许通过
- }
- }
- }
- // 3. 执行状态更新 (更新为休息中状态)
- updateStatus(userId, TechnicianStatusEnum.RESTING);
- if (!forceConfirm) {
- // 格式化成 yyyy-MM-dd 纯日期字符串
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
- String today = sdf.format(new Date());
- // 查询商户今日的最近考勤记录
- LambdaQueryWrapper<MerchantDailyAttendance> query = new LambdaQueryWrapper<>();
- query.eq(MerchantDailyAttendance::getMerchantId, userId)
- .eq(MerchantDailyAttendance::getAttendanceDate, today)
- .orderByDesc(MerchantDailyAttendance::getCreateTime);
- MerchantDailyAttendance update = merchantDailyAttendanceMapper.selectOne(query);
- if (update != null) {
- LocalDateTime localDateTime = update.getAttendanceStartTime().toInstant()
- .atZone(ZoneId.systemDefault())
- .toLocalDateTime();
- LambdaUpdateWrapper<MerchantDailyAttendance> updateWrapper = new LambdaUpdateWrapper<>();
- updateWrapper.eq(MerchantDailyAttendance::getId, update.getId())
- .set(MerchantDailyAttendance::getAttendanceEndTime, DateUtils.getNowDate())
- .set(MerchantDailyAttendance::getTotalWorkMinutes, Duration.between(localDateTime, LocalDateTime.now()).toMinutes())
- .set(MerchantDailyAttendance::getUpdateTime, DateUtils.getNowDate());
- merchantDailyAttendanceMapper.update(update, updateWrapper);
- }
- }
- } else {
- //更新状态为在线接单
- updateStatus(userId, TechnicianStatusEnum.ONLINE);
- // 插入今日考勤记录
- MerchantDailyAttendance merchantDailyAttendance = new MerchantDailyAttendance()
- .setMerchantId(userId.intValue())
- .setAttendanceDate(DateUtils.getNowDate())
- .setMerchantName(technician.getTeName())
- .setAttendanceStartTime(DateUtils.getNowDate())
- .setCreateBy(technician.getTeName())
- .setCreateTime(LocalDateTime.now());
- merchantDailyAttendanceMapper.insert(merchantDailyAttendance);
- }
- return Result.ok("状态已切换成功");
- }
- /**
- * 获取今天商户的考勤记录
- *
- * @param userId 技师ID
- * @return 考勤记录
- */
- private MerchantDailyAttendance getTodayAttendance(Long userId) {
- LambdaQueryWrapper<MerchantDailyAttendance> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(MerchantDailyAttendance::getMerchantId, userId)
- .eq(MerchantDailyAttendance::getAttendanceDate, DateUtils.getNowDate())
- .orderByDesc(MerchantDailyAttendance::getCreateTime)
- .last("LIMIT 1");
- return merchantDailyAttendanceMapper.selectOne(wrapper);
- }
- /**
- * 获取商户的累计工作时长
- *
- * @param userId 技师ID
- * @return 工作时长(分钟)
- */
- private long getWorkDuration(Long userId) {
- LambdaQueryWrapper<MerchantDailyAttendance> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(MerchantDailyAttendance::getMerchantId, userId)
- .eq(MerchantDailyAttendance::getAttendanceDate, DateUtils.getNowDate());
- List<MerchantDailyAttendance> attendances = merchantDailyAttendanceMapper.selectList(wrapper);
- if (attendances == null || attendances.isEmpty()) return 0;
- // 计算指定日期的总分钟数
- long totalMinutes = attendances.stream()
- .filter(attendance -> DateUtils.getNowDate().toString().equals(attendance.getAttendanceDate().toString()))
- .mapToLong(MerchantDailyAttendance::getTotalWorkMinutes)
- .sum();
- return totalMinutes;
- }
- /**
- * 获取商户的考勤规则
- *
- * @return 考勤规则
- */
- private AttendanceRule getAttendanceRule() {
- LambdaQueryWrapper<AttendanceRule> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(AttendanceRule::getIsDelete, 0);
- wrapper.eq(AttendanceRule::getWorkDurationRuleEnabled, 1);
- wrapper.last("LIMIT 1");
- return attendanceRuleMapper.selectOne(wrapper);
- }
- /**
- * 判断用户是否有生效中的技能
- *
- * @param userId 技师ID
- * @return true: 有可用技能, false: 无
- */
- public boolean hasActiveSkills(Long userId) {
- if (userId == null) return false;
- // 构建查询条件:用户ID匹配 AND 状态为已发布/生效中
- LambdaQueryWrapper<MaProject> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(MaProject::getMerchantId, userId)
- .eq(MaProject::getAuditStatus, 1); // 假设 1 代表 "生效/已审核"
- // 只要查到一条记录即返回 true
- return maProjectMapper.selectCount(wrapper) > 0;
- }
- /**
- * 判断用户是否有家庭地址(通常指设置为默认的地址)
- *
- * @param userId 技师ID
- * @return true: 有地址, false: 无
- */
- public boolean hasHomeAddress(Long userId) {
- if (userId == null) return false;
- // 构建查询条件:用户ID匹配 AND 是默认地址(可选) AND 未删除
- LambdaQueryWrapper<TAddress> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(TAddress::getMerchantId, userId)
- .eq(TAddress::getUserType, 2) // 商户类型
- .eq(TAddress::getIsDelete, 0); // 逻辑未删除
- // 统计数量是否大于0
- long count = addressMapper.selectCount(wrapper);
- return count > 0;
- }
- // 辅助方法:模拟获取用户
- private MaTechnician getTechnician(Long userId) {
- // ... DB查询逻辑
- LambdaQueryWrapper<MaTechnician> query = new LambdaQueryWrapper<>();
- query.eq(MaTechnician::getId, userId);
- return maTechnicianMapper.selectOne(query);
- }
- // 辅助方法:更新状态
- private void updateStatus(Long userId, TechnicianStatusEnum status) {
- LambdaUpdateWrapper<MaTechnician> update = new LambdaUpdateWrapper<>();
- update.eq(MaTechnician::getId, userId);
- update.set(MaTechnician::getPostState, status.getCode());
- // ... DB Update逻辑,同时记录上线/下线时间
- maTechnicianMapper.update(null, update);
- }
- /**
- * 后台待审核页面审核通过商户。
- *
- * @param id 商户ID
- * @param dto 待审核通过参数
- * @param loginUser 当前登录用户
- * @return 结果
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public int approvePendingMerchantAudit(Integer id, MaTechnicianPendingAuditSubmitDTO dto, LoginUser loginUser) {
- if (id == null) {
- throw new ServiceException("商户ID不能为空");
- }
- if (dto == null) {
- throw new ServiceException("审核参数不能为空");
- }
- String idCardFront = checkRequiredFileUrl(dto.getIdCardFront(), "身份证正面加密图片");
- String idCardBack = checkRequiredFileUrl(dto.getIdCardBack(), "身份证反面加密图片");
- String healthCertificate = checkRequiredFileUrl(dto.getHealthCertificate(), "健康证加密图片");
- String qualificationCertificate = checkRequiredFileUrl(dto.getQualificationCertificate(), "资格证加密图片");
- checkRequiredExpirationDate(dto.getIdCardExpirationDate(), "身份证到期时间");
- checkRequiredExpirationDate(dto.getHealthCertificateExpirationDate(), "健康证到期时间");
- checkRequiredExpirationDate(dto.getQualificationCertificateExpirationDate(), "资格证到期时间");
- String auditRemark = dto.getAuditRemark() == null ? "" : dto.getAuditRemark().trim();
- if (auditRemark.length() > AUDIT_REMARK_MAX_LENGTH) {
- throw new ServiceException("审核备注长度不能超过" + AUDIT_REMARK_MAX_LENGTH + "个字符");
- }
- MaTechnician existsMerchant = maTechnicianMapper.selectMerchantById(id);
- if (existsMerchant == null || !NOT_DELETED.equals(existsMerchant.getIsDelete())) {
- throw new ServiceException("商户不存在或已删除");
- }
- if (!Integer.valueOf(AUDIT_WAIT_REVIEW).equals(existsMerchant.getAuditStatus())) {
- throw new ServiceException("当前商户不是待审核状态,不能审核通过");
- }
- MaTechnician maTechnician = new MaTechnician();
- maTechnician.setId(id);
- /*maTechnician.setIdCard(String.join(",", idCardFront, idCardBack));
- maTechnician.setHealthCertificate(healthCertificate);
- maTechnician.setQualificationCertificate(qualificationCertificate);*/
- maTechnician.setIdCardExpirationDate(dto.getIdCardExpirationDate());
- maTechnician.setHealthCertificateExpirationDate(dto.getHealthCertificateExpirationDate());
- maTechnician.setQualificationCertificateExpirationDate(dto.getQualificationCertificateExpirationDate());
- maTechnician.setAuditStatus(AUDIT_APPROVED);
- maTechnician.setAuditRemark(auditRemark);
- maTechnician.setApproveTime(DateUtils.getNowDate());
- if (loginUser != null && loginUser.getUser() != null) {
- maTechnician.setUpdateBy(loginUser.getUser().getUserName());
- }
- maTechnician.setUpdateTime(DateUtils.getNowDate());
- int rows = maTechnicianMapper.approvePendingMerchantAuditById(maTechnician);
- if (rows <= 0) {
- throw new ServiceException("待审核商户审核通过失败");
- }
- return rows;
- }
- private String checkRequiredFileUrl(String value, String fieldName) {
- if (StringUtils.isBlank(value)) {
- throw new ServiceException(fieldName + "不能为空");
- }
- return value.trim();
- }
- private void checkRequiredExpirationDate(LocalDate value, String fieldName) {
- if (value == null) {
- throw new ServiceException(fieldName + "不能为空");
- }
- if (value.isBefore(LocalDate.now())) {
- throw new ServiceException(fieldName + "不能早于当前日期");
- }
- }
- /**
- * 技师待处理订单列表
- *
- * @param query
- * @return
- */
- @Override
- public List<WaitOrderDTO> listWaitOrder(WaitOrderQueryDTO query) {
- LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(TOrder::getStatus, OrderStatusEnum.WAIT_JD.getCode());
- // 1.查询所有待派未接单订单(status=待接单)
- List<TOrder> allWaitOrder = orderMapper.selectList(queryWrapper);
- if (CollectionUtils.isEmpty(allWaitOrder)) {
- return Collections.emptyList();
- }
- BigDecimal techLat = query.getTechLat();
- BigDecimal techLng = query.getTechLng();
- // 2.逐个计算两点距离(Haversine公式)
- List<WaitOrderDTO> dtoList = allWaitOrder.stream().map(order -> {
- WaitOrderDTO dto = new WaitOrderDTO();
- dto.setOrderId(order.getId());
- dto.setProjectName(getShortProjectName(order.getProjectName()));
- dto.setCustomerType("新客户");//无历史绑定默认新客,接单后再统计老客
- dto.setOrderCreateTime(order.getCreateTime());
- dto.setAppointTime(order.getAppointmentStartTime());
- dto.setTargetAddress(order.getContactAddressInfo());
- dto.setOrderLat(order.getUserLatitude());
- dto.setOrderLng(order.getUserLongitude());
- // 计算两点距离 单位:米
- BigDecimal disMeter = calcDistance(techLat, techLng, order.getUserLatitude(), order.getUserLongitude());
- dto.setDistanceMeter(disMeter);
- dto.setDistanceDesc(formatDistance(disMeter));
- return dto;
- }).collect(Collectors.toList());
- // 3.距离升序:近的排在最前面
- return dtoList.stream()
- .sorted(Comparator.comparing(WaitOrderDTO::getDistanceMeter))
- .collect(Collectors.toList());
- }
- /**
- * 接单
- *
- * @param req
- * @return
- */
- @Override
- public String acceptOrder(AcceptOrderReqDTO req) {
- Long techId = req.getTechId();
- Long orderId = req.getOrderId();
- //【校验1:订单是否已被其他技师接单】
- TOrder order = orderMapper.selectById(orderId);
- if (OrderStatusEnum.RECEIVED_ORDER.getCode().equals(order.getStatus())) {
- return OrderTipEnum.REPEAT_ORDER.getTip();
- }
- //【校验2:时间冲突校验(该技师已有已接单订单)】
- boolean isTimeConflict = checkOrderTimeConflict(techId, order);
- if (isTimeConflict) {
- String tip = String.format(OrderTipEnum.TIME_CONFLICT.getTip(),
- order.getStartTime().format(DateTimeFormatter.ofPattern("MM月dd日HH:mm")),
- order.getCompletedTime().format(DateTimeFormatter.ofPattern("MM月dd日HH:mm")));
- return tip;
- }
- //【校验3:技师休息状态】
- MaTechnician tech = maTechnicianMapper.selectById(techId);
- if (TechnicianStatusEnum.RESTING.getCode().equals(tech.getPostState())) {
- return OrderTipEnum.REST_CONFIRM.getTip();
- }
- // 正常接单,绑定技师ID到订单
- doAcceptOrder(techId, orderId);
- return OrderTipEnum.ALREADY_ACCEPT.getTip();
- }
- /**
- * 技师接单确认接单
- *
- * @param techId
- * @param orderId
- * @return
- */
- @Override
- public String confirmRestAccept(Long techId, Long orderId){
- LambdaUpdateWrapper<MaTechnician> update = new LambdaUpdateWrapper<>();
- update.eq(MaTechnician::getId, techId);
- update.set(MaTechnician::getPostState, TechnicianStatusEnum.ONLINE.getCode());
- maTechnicianMapper.update(null, update);
- doAcceptOrder(techId, orderId);
- return OrderTipEnum.ALREADY_ACCEPT.getTip();
- }
- /**
- * 技师拒绝接单
- *
- * @param req
- * @return
- */
- @Override
- public void refuseOrder(RefuseOrderReqDTO req){
- LambdaUpdateWrapper<TOrder> update = new LambdaUpdateWrapper<>();
- update.eq(TOrder::getId, req.getOrderId());
- update.set(TOrder::getStatus, OrderStatusEnum.REFUSE.getCode());
- update.set(TOrder::getRejectedReason, req.getRefuseReason());
- orderMapper.update(null, update);
- //拒单后订单重回待接单池,其他技师可刷到
- LambdaUpdateWrapper<TOrder> update2 = new LambdaUpdateWrapper<>();
- update2.eq(TOrder::getId, req.getOrderId());
- update2.set(TOrder::getStatus, OrderStatusEnum.WAIT_JD.getCode());
- update2.set(TOrder::getMerchantId, "");
- update2.set(TOrder::getRejectedReason, "");
- orderMapper.update(null, update2);
- }
- // =================工具方法=================
- /**
- * Haversine 计算经纬度距离 返回米
- */
- private BigDecimal calcDistance(BigDecimal lat1, BigDecimal lng1, BigDecimal lat2, BigDecimal lng2) {
- // 球面距离计算公式,地球半径6371000米
- // 可使用BigDecimal三角函数或数据库函数优化
- double latRad1 = Math.toRadians(lat1.doubleValue());
- double latRad2 = Math.toRadians(lat2.doubleValue());
- double lngRad1 = Math.toRadians(lng1.doubleValue());
- double lngRad2 = Math.toRadians(lng2.doubleValue());
- double dLat = latRad2 - latRad1;
- double dLng = lngRad2 - lngRad1;
- double a = Math.pow(Math.sin(dLat / 2), 2)
- + Math.cos(latRad1) * Math.cos(latRad2)
- * Math.pow(Math.sin(dLng / 2), 2);
- double dis = 2 * 6371000 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
- return BigDecimal.valueOf(dis).setScale(2, BigDecimal.ROUND_HALF_UP);
- }
- private String getShortProjectName(String name) {
- if (name != null && name.length() > 10) {
- return name.substring(0, 8) + "...";
- }
- return name;
- }
- private String formatDistance(BigDecimal distanceMeter) {
- BigDecimal km = distanceMeter.divide(new BigDecimal(1000), 2, BigDecimal.ROUND_HALF_UP);
- if (km.compareTo(BigDecimal.ONE) > 0) {
- return km + "km";
- } else {
- return distanceMeter.intValue() + "m";
- }
- }
- /**
- * 校验技师已有订单时间冲突(只查该技师已接单数据)
- */
- private boolean checkOrderTimeConflict(Long techId, TOrder newOrder) {
- LambdaQueryWrapper<TOrder> query = new LambdaQueryWrapper<>();
- query.eq(TOrder::getMerchantId, techId);
- query.eq(TOrder::getStatus, OrderStatusEnum.RECEIVED_ORDER.getCode());
- query.eq(TOrder::getStartTime, newOrder.getStartTime());
- List<TOrder> acceptedOrders = orderMapper.selectList(query);
- LocalDate newOrderDate = newOrder.getStartTime().toLocalDate();
- LocalDateTime newStart = newOrder.getStartTime();
- for (TOrder old : acceptedOrders) {
- if (!newOrderDate.isEqual(old.getCompletedTime().toLocalDate())) {
- continue;
- }
- LocalDateTime oldEnd = old.getCompletedTime();
- if (newStart.isBefore(oldEnd) || newStart.isEqual(oldEnd)) {
- return true;
- }
- }
- return false;
- }
- /**
- * 接单:给订单赋值技师ID
- */
- private void doAcceptOrder(Long techId, Long orderId) {
- TOrder update = new TOrder();
- update.setId(orderId);
- update.setMerchantId(techId);
- update.setStatus(OrderStatusEnum.RECEIVED_ORDER.getCode());
- orderMapper.updateById(update);
- }
- }
|