fhx
2024-10-09 efa120db12943c6acfaa7877ccfe8e31092468e0
Merge branch 'fhx-base-预约功能整合0808' into fhx-base-医助需求1008
5个文件已添加
3个文件已修改
3517 ■■■■■ 已修改文件
phi_platform_user/src/main/java/com/hx/phip/controller/appointment/AppAutoMateController.java 760 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
phi_platform_user/src/main/java/com/hx/phip/controller/appointment/AppointmentController.java 1255 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
phi_platform_user/src/main/java/com/hx/phip/controller/appointment/CrmSelfController.java 398 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
phi_platform_user/src/main/java/com/hx/phip/controller/appointment/CrmSelfV2Controller.java 438 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
phi_platform_user/src/main/java/com/hx/phip/service/EmployeeService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
phi_platform_user/src/main/java/com/hx/phip/service/appointment/AppointmentV2Service.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
phi_platform_user/src/main/java/com/hx/phip/service/appointment/impl/AppointmentV2ServiceImpl.java 617 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
phi_platform_user/src/main/java/com/hx/phip/service/impl/EmployeeServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
phi_platform_user/src/main/java/com/hx/phip/controller/appointment/AppAutoMateController.java
New file
@@ -0,0 +1,760 @@
package com.hx.phip.controller.appointment;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hx.common.BaseController;
import com.hx.exception.TipsException;
import com.hx.mybatisTool.SqlSentence;
import com.hx.phiappt.common.OperatorConstants;
import com.hx.phiappt.common.RoleType;
import com.hx.phiappt.constants.tool.appointment.TransferUtil;
import com.hx.phiappt.constants.tool.appointment.autoMate.AppAutoMateHandleUtil;
import com.hx.phiappt.constants.tool.appointment.autoMate.AppAutoMateV5Util;
import com.hx.phiappt.dao.mapper.*;
import com.hx.phiappt.model.*;
import com.hx.phiappt.vo.ApponintmentVo;
import com.hx.phiappt.vo.ProjectVo;
import com.hx.phiappt.vo.ShopWorkTimeVo;
import com.hx.phiappt.vo.appointment.autoMate.AppAutoMateVo;
import com.hx.phip.service.EmployeeRoleService;
import com.hx.phip.service.SystemParameterService;
import com.hx.resultTool.Result;
import com.hx.util.DateUtil;
import com.hx.util.StringUtils;
import com.hz.his.dto.appointment.AppointmentAutoMateDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
/**
 * author:fhx
 * Date:2021/3/24 18:43
 */
@RestController
@RequestMapping("/appointment/autoMate")
public class AppAutoMateController extends BaseController {
    private final Logger logger = LoggerFactory.getLogger(AppAutoMateController.class);
    @Resource
    private SystemParameterService systemParameterService;
    @Resource
    private EmployeeRoleService employeeRoleService;
    /** 自动匹配 - 混合项目 */
    @RequestMapping("/addApply/blend")
    public Result addApplyBland(@RequestBody AppointmentAutoMateDto dto)
    {
        logger.info("预约自动匹配请求参数:{}", JSONObject.toJSONString(dto));
        if(dto.getOpType() == null){
            throw new TipsException("操作人类型为空!");
        }
        if(StringUtils.isEmpty(dto.getShopId())) {
            throw new TipsException("请选择门店");
        }
        Shop shop = commonService.selectOneByKey(ShopMapper.class, dto.getShopId());
        if(shop == null) {
            throw new TipsException("找不到门店");
        }
        if(StringUtils.isEmpty(dto.getUserId())) {
            throw new TipsException("请选择用户");
        }
        Map<String, Object> values = new HashMap<>();
        SqlSentence sqlSentence = new SqlSentence();
        sqlSentence.setM(values);
        StringBuilder sql = new StringBuilder();
        values.put("userId", dto.getUserId());
        sqlSentence.setSqlSentence(" select u.*, cu.depId as shopId from user u left join corp_user cu on cu.id = u.hisCorpUserId WHERE u.id = #{m.userId} ");
        User user = commonService.selectOne(UserMapper.class, sqlSentence);
        if(user == null) {
            throw new TipsException("找不到用户");
        }
        Employee doctor = null;
        String doctorId = dto.getDoctorId();
        if(!StringUtils.isEmpty(doctorId)) {
            values.put("id", doctorId);
            values.put("roleUniqueStr", RoleType.UNIQUE_STR_DOCTOR);
            sql.setLength(0);
            sql.append(" select e.*, r.shopId from employee e ")
                    .append(" left join employee_role r on r.employeeId = e.id ")
                    .append(" WHERE e.id = #{m.id} and r.roleUniqueStr = #{m.roleUniqueStr} and e.isDel = 0 ")
                    .append(" group by r.roleUniqueStr ");
            sqlSentence.setSqlSentence(sql.toString());
            doctor = commonService.selectOne(EmployeeMapper.class, sqlSentence);
            if(doctor == null)
            {
                throw new TipsException("找不到医生");
            }
        }else{
            doctorId = null;
        }
        if(StringUtils.isEmpty(dto.getArriveDate(), dto.getStartTime())) {
            throw new TipsException("请选择到店日期和时间");
        }
        if(dto.getVisitType() == null) {
            throw new TipsException("请选择就诊类型");
        }
        if(dto.getAppType() == null) {
            throw new TipsException("请选择预约类型");
        }
        Integer waitDuration = dto.getWaitDuration();
        if(waitDuration == null || waitDuration < 0){
            waitDuration = 0;
        }
        Integer palsyDuration = dto.getPalsyDuration();
        if(palsyDuration == null || palsyDuration < 0){
            palsyDuration = 0;
        }
        if(dto.getSpecialUser() == null){
            dto.setSpecialUser(Appointment.NO);
        }
        if(dto.getIsMicApprove() == null){
            dto.setIsMicApprove(BaseEntity.NO);
        }
        if(dto.getIsSwitch() == null){
            dto.setIsSwitch(BaseEntity.NO);
        }
        if(dto.getRealMzTime() == null){
            dto.setRealMzTime(0);
        }
        //推荐匹配数量为空,则默认2
        if(dto.getMateNum() == null){
            dto.setMateNum(2);
        }
        //术前时间
        int sqTime = 0;
        LinkedHashMap<String, ProjectVo> pMap = new LinkedHashMap<>();
        Map<String, Integer> pNumMap = new HashMap<>();
        Map<String, Integer> pZlMap = new HashMap<>();
        List<Project> projectList = new ArrayList<>();
        String [] projectIdsArr = null;
        String [] projectNamesArr = null;
        int isDevice = 0;
        //是否再生项目
        String zsProjectId = "";
        boolean isZsP = false;
        //是否有非生美项目
        boolean isNotSm = false;
        //复查可以不选项目
        if(StringUtils.isEmpty(dto.getProjectJson()) && dto.getAppType() != Appointment.APP_TYPE_REVIEW)
        {
            throw new TipsException("请选择项目");
        }else {
            //再生项目逻辑,目前写死一个项目
            SystemParameter sp = systemParameterService.selectOneByName(SystemParameter.KEY_CRM_APPOINTMENT_PROJECT_ID);
            if(sp != null && StringUtils.noNull(sp.getParamValue())){
                zsProjectId = sp.getParamValue();
            }
            JSONArray pArr = JSONArray.parseArray(dto.getProjectJson());
            int len = pArr.size();
            JSONObject temp ;
            String id ;
            int num ;
            Project p ;
            projectIdsArr = new String[len];
            projectNamesArr = new String[len];
            for(int i = 0;i < len;i ++)
            {
                temp = pArr.getJSONObject(i);
                id = temp.getString("id");
                num = temp.getIntValue("num");
                if(StringUtils.isEmpty(id) || num <= 0)
                {
                    throw new TipsException("请选择正确项目和数量");
                }
                p = commonService.selectOneByKey(ProjectMapper.class, id);
                if(p == null || p.getIsDel().equals(BaseEntity.YES)  || p.getIsUp().equals(BaseEntity.NO))
                {
                    throw new TipsException("找不到项目或者项目下架");
                }
                projectList.add(p);
                pMap.put(id, new ProjectVo(p, num, dto.getShopId()));
                pNumMap.put(id, num);
                //判断治疗时间等于0,则默认5分钟
                pZlMap.put(id, p.getUseDuration() == null ? 0 : p.getUseDuration());
                //获取最大的术前准备时间
                if(p.getReadyDuration() > sqTime){
                    sqTime = p.getReadyDuration();
                }
                projectIdsArr[i] = id;
                projectNamesArr[i] = p.getName();
                if(p.getIsDevice().equals(BaseEntity.YES)){
                    isDevice++;
                }
                if(!isNotSm && p.getIsLifeBeauty().equals(BaseEntity.NO)){
                    isNotSm = true;
                }
                if(zsProjectId.equals(id)){
                    if(num > 1){
                        throw new TipsException("再生项目数量不能大于1!");
                    }
                    isZsP = true;
                }
            }
        }
        //判断如果包含了再生项目,
        if(isZsP && projectList.size() > 1){
            throw new TipsException("预约项目里面有再生项目,不能和其他项目同时预约!");
        }
        if(isNotSm && StringUtils.isEmpty(doctorId)){
            throw new TipsException("预约包含非生美项目,需选择预约医生!");
        }
        /////////////////////////////////////////////////
        //逻辑1
        //1.通过客户类型和客户等级查询出面诊、沟通、手续时间(默认5分钟)
        int mzTime = 15;
        int gtTime = 5;
        // int sxTime = 5;
        sql.setLength(0);
        sql.append(" select * from cure_set_time ");
        sql.append(" where isDel = #{m.isDel} and appType = #{m.appType} and memberLevelId = #{m.memberLevelId}");
        values.put("isDel", BaseEntity.NO);
        values.put("appType", dto.getAppType());
        values.put("memberLevelId", user.getMemberLevelId());
        sqlSentence.setSqlSentence(sql.toString());
        List<CureSetTime> cList = commonService.selectList(CureSetTimeMapper.class, sqlSentence);
        if(cList != null && cList.size() > 0){
            for(CureSetTime cst : cList){
                if(cst.getSetType() == CureSetTime.SET_TYPE_COM){
                    mzTime = cst.getDuration(); //面诊
                }
                // else if(cst.getSetType() == CureSetTime.SET_TYPE_COMMUNICATE){
                //     gtTime = cst.getDuration(); //沟通
                // }
                // else if(cst.getSetType() == CureSetTime.SET_TYPE_PROCEDURES){
                //     sxTime = cst.getDuration(); //手续
                // }
            }
        }
        if(mzTime > dto.getRealMzTime()){
            waitDuration = 0;
        }
        //判断沟通或手续是否为空,不为空就使用传值的
        gtTime = dto.getComDuration() != null ? dto.getComDuration() : gtTime;
        //术前时间(分钟) = 秒 /60
        sqTime = sqTime / 60;
        //2.选择项目的治疗时间
        //通过项目id和医生id查询判断是否有对应的医生项目治疗时间
        int zlTime = 0;
        int thisTime ;
        //医生id不为空
        if(!StringUtils.isEmpty(doctorId) && projectIdsArr != null){
            sql.setLength(0);
            sql.append("select * from doctor_project_cure_time where isDel = #{m.isDel} and doctorId = #{m.doctorId} and projectId = #{m.projectId}");
            sqlSentence.setSqlSentence(sql.toString());
            values.put("doctorId", doctorId);
            DoctorProjectCureTime ct;
            for(String projectId : projectIdsArr){
                values.put("projectId", projectId);
                ct = commonService.selectOne(DoctorProjectCureTimeMapper.class, sqlSentence);
                if(ct != null){
                    //设定的时间 * 数量
                    thisTime = ct.getDuration() * pNumMap.get(projectId);
                }else{
                    //项目治疗时间(秒) / 60 * 数量
                    thisTime = (pZlMap.get(projectId) / 60 * pNumMap.get(projectId));
                }
                zlTime += thisTime;
                pMap.get(projectId).setZlTime(thisTime);
            }
        }else{
            for(String projectId : projectIdsArr){
                //项目治疗时间(秒) / 60 * 数量
                thisTime = (pZlMap.get(projectId) / 60 * pNumMap.get(projectId));
                zlTime += thisTime;
                pMap.get(projectId).setZlTime(thisTime);
            }
        }
        //用户操作时,对应个别时间为0
        if(dto.getOpType() == OperatorConstants.OP_TYPE_USER){
//            mzTime = 0;
            //默认标准轮匹配的实际面诊时长=系统面诊时长
            dto.setRealMzTime(mzTime);
            gtTime = 0;
//            sqTime = 0;
            waitDuration = 0;
        }
        //再生项目时,其他时间都为0
        else if(isZsP){
            mzTime = 0;
            gtTime = 0;
            palsyDuration = 0;
            sqTime = 0;
            waitDuration = 0;
        }
//        //总时长 = 面诊 + 沟通 + 敷麻 + 术前 + 准备等待 + 治疗
//        int duration = mzTime + gtTime + palsyDuration + sqTime + waitDuration + zlTime;
        //////////////////////////////////////////////////////////
        //逻辑2
        Date st = DateUtil.parseString_1(dto.getArriveDate() + " " + dto.getStartTime() + ":00");
        //减去沟通时间
        String dayStr = DateUtil.formatDate(st);
        String projectIds = org.apache.commons.lang3.StringUtils.join(projectIdsArr, ",");
        String projectNames = org.apache.commons.lang3.StringUtils.join(projectNamesArr, "|");
        List<ApponintmentVo> aVoList = new ArrayList<>();
        JSONObject json = new JSONObject();
        Employee cu = null;
        //判断操作人是员工时
        if(dto.getOpType() == OperatorConstants.OP_TYPE_EMPLOYEE && StringUtils.noNull(dto.getOpRoleId())){
            cu = employeeRoleService.selectEmployeeById(dto.getOpRoleId());
        }
        String err;
        //非MIC、非勾选转疗、且不是用户所属顾问门店的预约,才检查转疗限制逻辑
        if(dto.getIsMicApprove() == BaseEntity.NO
                && dto.getIsSwitch() == BaseEntity.NO
                && !StringUtils.isEmpty(user.getShopId())
                && !dto.getShopId().equals(user.getShopId())){
            err = TransferUtil.checkTransferNotAccepted(commonService
                    , user, shop, st, doctor, cu, projectList);
            if(err != null){
                throw new TipsException(err);
            }
            //修改为转疗
            dto.setIsSwitch(BaseEntity.YES);
        }
        //-----迁移到新增/修改预约进行判断
        Integer occupySwitch = dto.getIsSwitch();
        //检查是否不占用转疗名额
        if(BaseEntity.YES.equals(occupySwitch)){
            occupySwitch = TransferUtil.checkTransferNotOccupy(commonService
                    , user.getUserLevel(), dto.getShopId() , st, doctorId
                    , cu , projectList);
        }
        //非MIC检查下是否符合要求
        if(dto.getIsMicApprove() == BaseEntity.NO){
            //先检查下对应条件是否符合要求:1项目类型、2门店预约人数、3门店最大转疗人数、4检查设备、5医生预约人数
            err = AppAutoMateHandleUtil.checkLimit(commonService, shop, doctorId, st, dayStr, projectIdsArr, dto.getIsSwitch(), occupySwitch, dto.getAptId());
            if(!StringUtils.isEmpty(err)) {
                json.put("list", aVoList);
                json.put("sqTime", sqTime);
                json.put("zlTime", zlTime);
                json.put("mzTime", mzTime);
                json.put("isSwitch", dto.getIsSwitch());
                json.put("occupySwitch", occupySwitch);
                json.put("errMsg", err);
                return Result.success(json);
            }
        }
        //处理项目可预约时间
        List<Date> canAppList = AppAutoMateHandleUtil.getProjectCanAppTime(commonService, projectList, st);
//        List<Date> canAppList = null;
        //医生时间
        List<DoctorTime> doctorTimeList = AppAutoMateHandleUtil.selectDoctorTime(commonService, dto.getShopId(), doctorId, dayStr, dto.getAptId());
        //过滤出医生的预约占用的特殊用户时间
        List<DoctorTime> specialTimeList = new ArrayList<>();
        if(dto.getSpecialUser() == Appointment.YES){
            specialTimeList = doctorTimeList.stream()
                    .filter(s->s.getSpecialUser() == Appointment.YES
                            && s.getApptType() != DoctorTime.APPT_TYPE_NONE)
                    .collect(Collectors.toList());
        }
        //判断如果是mic通用
        if(dto.getIsMicApprove() == BaseEntity.YES){
                return Result.success(AppAutoMateHandleUtil.MICList(commonService
                        , aVoList, st, dto.getRealMzTime(), gtTime, palsyDuration
                        , sqTime, zlTime, waitDuration, projectIds, projectNames
                        ,isDevice, dto.getShopId(), dayStr, dto.getAptId(), pMap, specialTimeList));
        }
        //没有医生为全部生美项目
        if(StringUtils.isEmpty(doctorId)){
            return Result.success(AppAutoMateHandleUtil.SMList(commonService, aVoList, st, dto.getRealMzTime(), gtTime, palsyDuration
                    , sqTime, zlTime, waitDuration, projectIds, projectNames
                    ,isDevice, dto.getShopId(), dayStr, dto.getAptId(), pMap, dto.getIsSwitch(),occupySwitch, canAppList
                    , specialTimeList));
        }
        //查询当天是否有活动日
        values.put("shopId", dto.getShopId());
        values.put("dayStr", dayStr);
        sqlSentence.setSqlSentence("select * from activity_day where isDel = #{m.isDel} and shopId = #{m.shopId} and dayStr = #{m.dayStr}");
        ActivityDay activityDay = commonService.selectOne(ActivityDayMapper.class, sqlSentence);
        //门店营业时间
        ShopWorkTimeVo shopWorkTimeVo = new ShopWorkTimeVo(shop, dayStr);
//        //初始日期(存储判断)
//        String initDayStr = dayStr;
//        //初始开始时间(如09:00)
//        String initStartTime = dto.getStartTime();
        TreeMap<String, Integer> timeMap = null;
        // 医生id为空返回门店时间
        // if(StringUtils.isEmpty(doctorId)){
        //     timeMap = AppAutoMateV5Util.shopWorkTimeSet(shopWorkTimeVo);
        // }
        //门店特殊时间
        List<ShopTime> shopTimeList = AppAutoMateHandleUtil.selectShopTime(commonService, dto.getShopId(), dayStr, dto.getAppType(), user.getUserLevel(), user.getStarLevel());
        //时间处理次数:1第一轮医生正常时间、2第二轮医生开头5分钟可预约、3第三轮医生结束5分钟可预约
        int handleNum = 1;
        //向上还是向下:0标准1向下2向上(向下或向上中间可加等待时间)
        int upOrDown = 0;
        //按天分隔类型:0不分隔、1上午、2下午
        int daySpaceType = dto.isDaySpace() ? 1 : 0;
        //第一轮匹配
        AppAutoMateVo mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                , doctorId, dto.getShopId(), handleNum, canAppList);
        timeMap = mateData.getTimeMap();
        err = mateData.getErrMsg();
        if(timeMap == null || timeMap.size() <= 0){
            throw new TipsException("当前预约医生预约日期无排班时间,无法选择系统推荐时间,请重新选择预约医生");
        }
        ////////////////////////////////////////////////////////////////////////
        //判断用户选择的时间是否符合
        //是否走扣减处理(默认true),即扣减医生上下5分钟逻辑
        boolean deductHandle = true;
        //如果是是再生项目、或预约特殊类型为特殊客户,则不走扣减处理
        if(isZsP || dto.getSpecialUser() == Appointment.YES){
            deductHandle = false;
        }
        ApponintmentVo aVo = null;
//        ApponintmentVo aVo2 = null;
        String deviceErr = null;
        //从医生可用时间里面,拼接出当天最小可以时间
        Date minStartTime = DateUtil.parseString_1(dto.getArriveDate() + " " + timeMap.keySet().iterator().next() + ":00");
        Date upStratTime = st;
        Calendar cs = Calendar.getInstance();
        //判断是否有项目可预约时间限制
        int isCanApp = canAppList == null || canAppList.size() < 2 ? 0 : 1;
        //查询医生某个时间段有多少治疗数量sql
        sql.setLength(0);
        sql.append(" select COUNT(1) from doctor_time where ")
                .append(" where isDel = 0 and shopId = '").append(dto.getShopId()).append("' ")
                .append(" and apptType = 2 and timeType in (2, 3) ")
                .append(" and ( #{m.zlStartTime} BETWEEN startTime and endTime ")
                .append(" or #{m.zlEndTime} BETWEEN startTime and endTime) ");
        String doctorTimeAppNumSql = sql.toString();
        //超过2个停止循环
        while(aVoList.size() < dto.getMateNum() && StringUtils.isEmpty(err)){
            //每次匹配都判断一下是否要做上下午时间处理
            mateData = AppAutoMateHandleUtil.handleTimeToDaySpace(timeMap, dto.getArriveDate(), daySpaceType, palsyDuration, st);
            timeMap = mateData.getTimeMap();
            st = mateData.getStartTime(); //如果是上下午区分的,时间开始时间要重新获取才能符合上下午时间的分隔
//            logger.info("开始时间:{}", DateUtil.formatDate_2(st));
//            logger.info("daySpaceType:{}", daySpaceType);
//            logger.info("upOrDown:{}", upOrDown);
//            logger.info("handleNum:{}", handleNum);
//            logger.info("时间:{}", JSONObject.toJSONString(timeMap));
            //判断如果时特殊客户预约,且非第一次处理和没有符合的时间,则直接提示不继续处理
            if(upOrDown == 0){
                //标准轮(使用实际面诊时长)
                aVo = AppAutoMateV5Util.getAppTimeBlend(timeMap, upStratTime, gtTime, dto.getRealMzTime(), palsyDuration, sqTime, zlTime, waitDuration, pMap, isCanApp);
            } else if(upOrDown == 1){
                //向下匹配预约时间(使用配置的面诊时长)
                aVo = AppAutoMateV5Util.getAppTimeByDownV2(timeMap, st, gtTime, mzTime, palsyDuration, sqTime, zlTime, 0, 50, pMap, isCanApp);
            }else if(upOrDown == 2){
                //向上匹配预约时间(使用配置的面诊时长)
                aVo = AppAutoMateV5Util.getAppTimeByUpV2(timeMap, upStratTime, gtTime, mzTime, palsyDuration, sqTime, zlTime, 0, 50, pMap, isCanApp);
            }
            //为空,表示当天已经没有时间段可以预约
            if(aVo == null && upOrDown != 0)
            {
                if (aVoList.size() < dto.getMateNum() && handleNum == 1 && deductHandle) {
                    //第二轮(治疗开始时间+10分钟)
                    handleNum = 2;
                    mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                            , doctorId, dto.getShopId(), handleNum, canAppList);
                    timeMap = mateData.getTimeMap();
                } else if (aVoList.size() < dto.getMateNum() && handleNum == 2 && deductHandle) {
                    //第三轮(治疗结束时间-10分钟)
                    handleNum = 3;
                    mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                            , doctorId, dto.getShopId(), handleNum, canAppList);
                    timeMap = mateData.getTimeMap();
                }
                //如果是处理到:向上处理+没满足2个+(第三轮后 或 非扣减处理),则跑向下处理(+等待时间)
                else if(upOrDown == 2 && aVoList.size() < dto.getMateNum() && (handleNum == 3 || !deductHandle)) {
                    //修改向上匹配时间
                    upOrDown = 1;
                    handleNum = 1;
                    //还原正常时间先
                    mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                            , doctorId, dto.getShopId(), handleNum, canAppList);
                    timeMap = mateData.getTimeMap();
                    // } else if(upOrDown == 2 && handleNum == 3){
                }
                //判断如果上午分隔时间执行完,则进行下午分隔处理
                else if(daySpaceType == 1){
                    daySpaceType = 2;
                    //重置标准匹配时间
                    upOrDown = 0;
                    handleNum = 1;
                    mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                            , doctorId, dto.getShopId(), handleNum, canAppList);
                    timeMap = mateData.getTimeMap();
                }else {
                    if(StringUtils.isEmpty(err)){
                        err = mateData.getErrMsg();
                    }
                    if(StringUtils.isEmpty(err) && dto.getSpecialUser() == Appointment.YES && StringUtils.noNull(doctorId)){
                        err = "(特殊用户预约)系统匹配不到医生可用时间!";
                    }else if(StringUtils.isEmpty(err)){
                        err = deviceErr != null ? deviceErr : "系统匹配不到医生可用时间,或项目有可预约时间限制";
                    }
                    break;
                }
            }
            else if(aVo != null)
            {
                //判断开始时间是否小于门店开始时间
                boolean isSmaller = aVo.getStartTime().compareTo(shopWorkTimeVo.getmStartTime()) == -1;
                deviceErr = null;
                //判断需不需要设备判断,不需要不检查设备逻辑
                if(isDevice > 0){
                    //检查设备时间
                    deviceErr = AppAutoMateHandleUtil.checkDevice(commonService, dto.getShopId(), dayStr, aVo, dto.getAptId());
                }
                if(StringUtils.isEmpty(deviceErr) && !isSmaller)
                {
                    //判断活动日不为空,并且匹配时间不超过活动日时间
                    if(activityDay != null
                            && aVo.getStartTime().compareTo(activityDay.getStartTime()) > -1
                            && aVo.getEndTime().compareTo(activityDay.getEndTime()) < 1)
                    {
                        aVo.setActivityDayId(activityDay.getId());
                    }
                    boolean isCustomerNum=true;
                    if(shop.getSameTimeCustomerNum()!=null &&shop.getSameTimeCustomerNum()>0){
                        values.put("zlStartTime", aVo.getZlStartTime());
                        values.put("zlEndTime", aVo.getEndTime());
                        sqlSentence.setSqlSentence(doctorTimeAppNumSql);
                        int count = commonService.selectCountSql(sqlSentence);
                        logger.info("=====================预约订单自动获取时间===========当前时间段的订单数:"+count);
                        logger.info(DateUtil.formatDate_2(aVo.getZlStartTime())+"=="+DateUtil.formatDate_2(aVo.getEndTime())+"=="+shop.getSameTimeCustomerNum()+"=="+count);
                        if(count>=shop.getSameTimeCustomerNum()){
                            isCustomerNum=false;
                        }
                    }
                    if(isCustomerNum){
                        //判断相同向下匹配
                        if(aVoList.size() > 0)
                        {
                            //遍历判断匹配时间是否重复,
                            boolean isRepeat = false;
                            for(ApponintmentVo apponintmentVo : aVoList){
                                if(
                                        (apponintmentVo.getStartTime().compareTo(aVo.getStartTime()) == 0 && apponintmentVo.getEndTime().compareTo(aVo.getEndTime()) == 0)
                                    // || (aVo2.getMzStartTime().compareTo(aVo.getMzStartTime()) == 0 && aVo2.getMzEndTime().compareTo(aVo.getMzEndTime()) == 0)
                                    // || (aVo2.getZlStartTime().compareTo(aVo.getZlStartTime()) ==0 && aVo2.getEndTime().compareTo(aVo.getEndTime()) == 0)
                                ){
                                    isRepeat = true;
                                   break;
                                }
                            }
                            //不重复则,存储对应可用
                            if(!isRepeat){
                                aVo.setHandleNum(handleNum);
                                aVo.setUpOrDown(upOrDown);
                                aVo.setProjectIds(projectIds);
                                aVo.setProjectNames(projectNames);
                                aVo.handleTips(handleNum, upOrDown);
                                aVo.setDaySpaceType(daySpaceType);
                                aVoList.add(aVo);
                                //进入则表示上午已有1个,重置标准匹配下午时间
                                if(daySpaceType == 1){
                                    daySpaceType = 2;
                                    upOrDown = 0;
                                    handleNum = 1;
                                    mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                                            , doctorId, dto.getShopId(), handleNum, canAppList);
                                    timeMap = mateData.getTimeMap();
                                    continue;
                                }else if(daySpaceType == 2){
                                    //如果是下午分隔的可以跳过
                                    break;
                                }
                            }else{
                                //重复则向下匹配
                                upOrDown = 1;
                            }
                        }else{
                            aVo.setHandleNum(handleNum);
                            aVo.setUpOrDown(upOrDown);
                            aVo.setProjectIds(projectIds);
                            aVo.setProjectNames(projectNames);
                            aVo.handleTips(handleNum, upOrDown);
                            aVo.setDaySpaceType(daySpaceType);
                            aVoList.add(aVo);
                            //进入则表示上午已有1个,重置标准匹配下午时间
                            if(daySpaceType == 1){
                                daySpaceType = 2;
                                upOrDown = 0;
                                handleNum = 1;
                                mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                                        , doctorId, dto.getShopId(), handleNum, canAppList);
                                timeMap = mateData.getTimeMap();
                                continue;
                            }else if(daySpaceType == 2){
                                //如果是下午分隔的可以跳过
                                break;
                            }
                        }
                    }
                }
                //如果检查返回有不符合的条件
                if(upOrDown == 1 && aVoList.size() < dto.getMateNum()){
                    //向下
                    //已面诊开始时间为开头加5分钟重新判断
                    cs.setTime(aVo.getMzStartTime());
                    // cs.setTime(aVo.getEndTime());
                    cs.add(Calendar.MINUTE, 5);
                    st = cs.getTime();
                }
                // else if(upOrDown == 2 && aVoList.size() == 1){
                //     break;
                else if(upOrDown == 2 && aVoList.size() < dto.getMateNum()){
                    //向上
                    //已初始向上开始时间为开头减5分钟重新判断
                    cs.setTime(aVo.getMzStartTime());
                    cs.add(Calendar.MINUTE, -5);
                    upStratTime = cs.getTime();
                    // st = cs.getTime();
                    //开始时间小于医生最小工作时间时
                    if(upStratTime.compareTo(minStartTime) < 0 ){
                        upOrDown = 1;
                        handleNum = 1;
                        mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                                , doctorId, dto.getShopId(), handleNum, canAppList);
                        timeMap = mateData.getTimeMap();
                    }
                }
                // dayStr = DateUtil.formatDate(st);
            }
            //判断标准匹配处理
            if(upOrDown == 0){
                //标准匹配没有跑上下减5分钟逻辑,且还没走到第三轮和走扣减逻辑
                if(aVo == null && handleNum < 3 && deductHandle){
                    if (handleNum == 1 && deductHandle) {
                        //第二轮(治疗开始时间+10分钟)
                        handleNum = 2;
                        mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                                , doctorId, dto.getShopId(), handleNum, canAppList);
                        timeMap = mateData.getTimeMap();
                    } else if (handleNum == 2 && deductHandle) {
                        //第二轮(治疗结束时间-10分钟)
                        handleNum = 3;
                        mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                                , doctorId, dto.getShopId(), handleNum, canAppList);
                        timeMap = mateData.getTimeMap();
                    }
                } else {
                    //标准匹配又符合的 或 已走完第三轮 或不走扣减逻辑,则向下匹配重新匹配
                    upStratTime = aVo != null ? aVo.getMzStartTime() : st;
                    handleNum = 1;
                    upOrDown  = 2;
                    mateData = AppAutoMateHandleUtil.handleTime(commonService, shopWorkTimeVo, activityDay, projectIdsArr, doctorTimeList, shopTimeList
                            , doctorId, dto.getShopId(), handleNum, canAppList);
                    timeMap = mateData.getTimeMap();
                }
            }
        }
        json.put("list", aVoList);
        json.put("sqTime", sqTime);
        json.put("zlTime", zlTime);
        json.put("mzTime", mzTime);
        json.put("isSwitch", dto.getIsSwitch());
        json.put("occupySwitch", occupySwitch);
        if(!StringUtils.isEmpty(err)){
            json.put("errMsg", err);
        }else{
            json.put("errMsg", deviceErr);
        }
        logger.info("预约匹配返回结果:{}", json.toString());
        return Result.success(json);
    }
}
phi_platform_user/src/main/java/com/hx/phip/controller/appointment/AppointmentController.java
@@ -3,24 +3,29 @@
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hx.common.BaseController;
import com.hx.guide.util.AppointmentListUtil;
import com.hx.exception.TipsException;
import com.hx.guide.util.CommonQueryUtil;
import com.hx.mybatisTool.SqlSentence;
import com.hx.phiappt.common.OperatorConstants;
import com.hx.phiappt.common.RoleType;
import com.hx.phiappt.common.UserStatusConstants;
import com.hx.phiappt.constants.tool.appointment.AppAutoMateV5Util;
import com.hx.phiappt.constants.tool.appointment.AppointmentTool;
import com.hx.phiappt.constants.tool.appointment.TransferUtil;
import com.hx.phiappt.dao.mapper.*;
import com.hx.phiappt.model.*;
import com.hx.phiappt.util.AppointmentV2Util;
import com.hx.phiappt.vo.ProjectVo;
import com.hx.phiappt.vo.OperatorVo;
import com.hx.phip.service.EmployeeRoleService;
import com.hx.phip.service.EmployeeService;
import com.hx.phip.service.SystemParameterService;
import com.hx.phip.service.appointment.AppointmentService;
import com.hx.phip.service.appointment.AppointmentV2Service;
import com.hx.resultTool.Result;
import com.hx.util.DateUtil;
import com.hx.util.StringUtils;
import com.hz.his.dto.appointment.AppointmentCrmAddDto;
import com.platform.exception.PlatTipsException;
import com.platform.resultTool.PlatformCode;
import com.hz.his.dto.appointment.AppointmentV2Dto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -33,6 +38,7 @@
 * @USER: fhx
 * @DATE: 2023/7/5
 **/
@Slf4j
@RestController
@RequestMapping("/appointment")
public class AppointmentController extends BaseController {
@@ -41,238 +47,1075 @@
    private AppointmentService appointmentService;
    @Resource
    private SystemParameterService systemParameterService;
    @Resource
    private AppointmentV2Service appointmentV2Service;
    @Resource
    private EmployeeRoleService employeeRoleService;
    @Resource
    private EmployeeService employeeService;
    /** CRM预约 */
    @RequestMapping("/crm/app")
    public Result crmApp(@RequestBody(required = false) AppointmentCrmAddDto dto) {
    /** 新增预约 */
    @RequestMapping("/add")
    public Result add(@RequestBody AppointmentV2Dto dto) {
        log.info("新增预约参数:{}", JSONObject.toJSONString(dto));
        //获取操作人信息
        OperatorVo operatorVo = getOperatorInfo(dto);
        if (dto == null) {
             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "请求参数为空!");
        if (StringUtils.isEmpty(dto.getShopId())) {
            throw new TipsException("请选择门店!");
        }
        if(dto.getHandleType() == null){
             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "处理类型不能为空!");
        if (StringUtils.isEmpty(dto.getUserId())) {
            throw new TipsException("请选择用户!");
        }
        Appointment appointment = null;
        //判断处理类型处理类型(1新增2编辑3取消)
        if(dto.getHandleType() == 2 || dto.getHandleType() == 3){
            if(StringUtils.isEmpty(dto.getAppointmentId())){
                throw new PlatTipsException(PlatformCode.ERROR_TIPS, "预约标识不能为空!");
            }
        if (StringUtils.isEmpty(dto.getStartTime())) {
            throw new TipsException("请选择预约时间!");
        }
            appointment = commonService.selectOneByKey(AppointmentMapper.class, dto.getAppointmentId());
            if(appointment == null){
                throw new PlatTipsException(PlatformCode.ERROR_TIPS, "查询不到预约信息!");
            }
        if (dto.getAddMode() == null) {
            throw new TipsException("预约方式为空!");
        }
            //如果处理类型为取消,则直接调用取消逻辑然后返回
            if(dto.getHandleType() == 3){
                appointmentService.cancel(appointment, dto.getRemark(), Appointment.CANCEL_TYPE_CRM_USER, null);
                return Result.success();
        if (dto.getVisitType() == null) {
            throw new TipsException("请选择就诊类型!");
        }
        if (dto.getAppType() == null) {
            throw new TipsException("请选择预约类型!");
        }
        if (dto.getIsMicApprove() == BaseEntity.YES){
            if(dto.getAddMode() == Appointment.ADD_MODE_ADVISER_V2_3 && StringUtils.isEmpty(dto.getImgUrls())){
                throw new TipsException("MIC同意必须选择图片!");
            }
        }
        if(StringUtils.isEmpty(dto.getUserId())){
             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "用户标识为空!");
        String corpUserId = StringUtils.isEmpty(dto.getCorpUserId()) ? null : dto.getCorpUserId();
        //员工预约时
        Employee employee = checkEmployee(operatorVo, corpUserId);
        Shop shop = commonService.selectOneByKey(ShopMapper.class, dto.getShopId());
        if(shop == null) {
            throw new TipsException("查询不到预约门店信息!");
        }
        if(StringUtils.isEmpty(dto.getShopId())){
             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "门店标识为空!");
        User user = commonService.selectOneByKey(UserMapper.class, dto.getUserId());
        if(user == null) {
            throw new TipsException("找不到用户");
        }
        if(StringUtils.isEmpty(dto.getDoctorId())){
             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "医生标识为空!");
        //用户自助预约时,判断使用用户所属顾问为预约顾问
        if(dto.getAddMode() == Appointment.ADD_MODE_CRM_SELF && StringUtils.noNull(user.getHisCorpUserId())){
            corpUserId = user.getHisCorpUserId();
        }
        if(StringUtils.isEmpty(dto.getStartTime())){
             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "开始时间为空!");
        }
        User user = commonService.selectOneByKeyBlob(UserMapper.class, dto.getUserId());
        if (user == null) {
             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到用户信息!");
        }
        Shop shop = commonService.selectOneByKeyBlob(ShopMapper.class, dto.getShopId());
        if (shop == null) {
             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到门店信息!");
        }
        Employee employee = commonService.selectOneByKeyBlob(EmployeeMapper.class, dto.getShopId());
        if (shop == null) {
            throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到医生信息!");
        }
        //目前写死一个项目
        SystemParameter sp = systemParameterService.selectOneByName(SystemParameter.KEY_CRM_APPOINTMENT_PROJECT_ID);
        if(sp == null || StringUtils.isEmpty(sp.getParamValue())){
            throw new PlatTipsException(PlatformCode.ERROR_TIPS, "还未配置项目信息!");
        }
        Project project = commonService.selectOneByKey(ProjectMapper.class, sp.getParamValue());
        if(project == null){
             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到项目信息!");
        }
        if(project == null || project.getIsDel() == BaseEntity.YES || project.getIsUp() == BaseEntity.NO)
        String doctorId = dto.getDoctorId();
        if(!StringUtils.isEmpty(doctorId) && !"null".equals(doctorId))
        {
             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到项目或者项目下架!");
            Employee doctor = employeeService.selectOneByIdAndRole(doctorId, RoleType.UNIQUE_STR_DOCTOR);
            if(doctor == null)
                throw new TipsException("找不到医生");
        }else{
            doctorId = null;
        }
        //组装json数组
        JSONArray arr = new JSONArray();
        JSONObject temp = new JSONObject();
        temp.put("id", project.getId());
        temp.put("num", 1);
        temp.put("isLifeBeauty", project.getIsLifeBeauty());
        temp.put("name", project.getName());
        temp.put("palsyDuration", project.getPalsyDuration());
        temp.put("isPalsy", project.getPalsyDuration() > 0 ? 1 : 0);
        temp.put("useDuration", project.getUseDuration());
        arr.add(temp);
        if(dto.getSpecialUser() == null) {
            dto.setSpecialUser(BaseEntity.NO);
        }
        String [] projectIdsArr =  new String[arr.size()];
        String [] projectNamesArr = new String[arr.size()];
        Map<String, Integer> pNumMap = new HashMap<>();
        Map<String, Integer> pZlMap = new HashMap<>();
        String id = null;
        int num = 0;
        int zlTime = 0;
        if(dto.getIsSwitch() == null) {
            dto.setIsSwitch(BaseEntity.NO);
        }
        if(dto.getObeyDoctor() == null) {
            dto.setObeyDoctor(BaseEntity.NO);
        }
        List<AppointmentProject> apList = new ArrayList<>();
        LinkedHashMap<String, ProjectVo> pMap = new LinkedHashMap<>();
        for(int i = 0; i < arr.size(); i ++)
        if(dto.getObeyTime() == null) {
            dto.setObeyTime(BaseEntity.NO);
        }
        if(dto.getIsSendMsg() == null) {
            dto.setIsSendMsg(BaseEntity.NO);
        }
        if(dto.getIsArriveSendMsg() == null) {
            dto.setIsArriveSendMsg(BaseEntity.NO);
        }
        if(dto.getWaitTime() == null) {
            dto.setWaitTime(BaseEntity.NO);
        }
        if(dto.getGtTime() == null || dto.getMzTime() == null || dto.getMfTime() == null || dto.getSqTime() == null || dto.getZlTime() == null || dto.getWaitTime() == null
                || dto.getGtTime() < 0 || dto.getMzTime() < 0 || dto.getMfTime() < 0 || dto.getSqTime() < 0 || dto.getZlTime() < 0 || dto.getWaitTime() < 0)
        {
            temp = arr.getJSONObject(i);
            id = temp.getString("id");
            num = temp.getIntValue("num");
            if(StringUtils.isEmpty(id) || num <= 0)
            {
                 throw new PlatTipsException(PlatformCode.ERROR_TIPS, "请选择正确项目和数量");
            throw new TipsException("请填写正确的沟通时间、面诊时间、麻敷时间、术前准备时间、治疗准备开始时间、治疗时间");
        }
        Integer duration = dto.getGtTime() + dto.getMzTime() + dto.getMfTime() + dto.getSqTime() + dto.getZlTime() + dto.getWaitTime();
        Date st = DateUtil.parseString(dto.getStartTime(), "yyyy-MM-dd HH:mm:ss");
        Date et = DateUtil.parseString(dto.getEndTime(), "yyyy-MM-dd HH:mm:ss");
        //结束时间为空则根据总时长算出来
        if(et == null){
            Calendar ca = Calendar.getInstance();
            ca.setTime(st);
            ca.add(Calendar.MINUTE, duration);
            et = ca.getTime();
        }
        //检测用户创建预约
        checkUserAdd(dto.getAddMode(), dto.getDoctorId(), st, et);
        //预约关联项目
        List<Project> projectList = new ArrayList<>();
        List<AppointmentProject> apList = getAppointmentProjectList(dto, projectList);
        //医生限制数量校验
        AppointmentTool.doctorLimitPreNumCheck(dto.getShopId(),null,doctorId,st,commonService);
        //检测非繁忙项目
        int rulePreType = AppointmentTool.preProjectNonBusy(dto.getShopId(),null,st, projectList,null,commonService);
        //检查是否不占用转疗名额
        Integer occupySwitch = dto.getIsSwitch();
        if(BaseEntity.YES.equals(occupySwitch)){
            occupySwitch = TransferUtil.checkTransferNotOccupy(commonService
                    , user.getUserLevel(), dto.getShopId() , st, doctorId, employee , projectList);
        }
        Map<String, Object> map = new HashMap<>();
        SqlSentence sqlSentence = new SqlSentence();
        sqlSentence.setM(map);
        String dayStr = DateUtil.formatDate(st);
        Appointment appointment = new Appointment();
        appointment.setShopId(dto.getShopId());
        appointment.setCorpUserId(corpUserId);
        appointment.setDoctorId(doctorId);
        appointment.setObeyDoctor(dto.getObeyDoctor());
        appointment.setUserId(dto.getUserId());
        appointment.setStartTime(st);
        appointment.setEndTime(et);
        appointment.setDuration(duration);
        appointment.setVisitType(dto.getVisitType());
        appointment.setAppType(dto.getAppType());
        appointment.setComDuration(dto.getGtTime());
        appointment.setDocComDuration(dto.getMzTime());
        appointment.setReadyDuration(dto.getSqTime());
        appointment.setProDuration(0);
        appointment.setPalsyDuration(dto.getMfTime());
        appointment.setTreatDuration(dto.getZlTime());
        appointment.setRemark(dto.getRemark());
        appointment.setObeyTime(dto.getObeyTime());
        appointment.setSpecialUser(dto.getSpecialUser());
        appointment.setOccupySwitch(occupySwitch);
        appointment.setRulePreType(rulePreType);
        appointment.setDayStr(dayStr);
        appointment.setMonthStr(dayStr.substring(0, 6));
        appointment.setIsSwitch(dto.getIsSwitch());
        appointment.setIsAnaesthesia(dto.getMfTime() > 0 ? BaseEntity.YES : BaseEntity.NO);
        appointment.setIsMicApprove(dto.getIsMicApprove());
        //新创建人信息
        appointment.setCreateManId(operatorVo.getOpId());
        appointment.setCreateManName(operatorVo.getOpName());
        if(operatorVo.getOpType() == OperatorConstants.OP_TYPE_EMPLOYEE){
            appointment.setCreateManType(Appointment.CREATE_MAN_TYPE_EMPLOYEE);
            appointment.setCreateCorpUserId(operatorVo.getOpId());
            appointment.setCreateRoleId(operatorVo.getOpRoleId());
            appointment.setCreateRoleStr(operatorVo.getRoleStr());
        }else if(operatorVo.getOpType() == OperatorConstants.OP_TYPE_USER){
            appointment.setCreateManType(Appointment.CREATE_MAN_TYPE_USER);
        }else {
            appointment.setCreateManType(Appointment.CREATE_MAN_TYPE_UNKNOWN);
        }
        appointment.setImgUrls(dto.getImgUrls());
        appointment.setProjectIds(dto.getProjectIds());
        appointment.setProjectNames(dto.getProjectNames());
        appointment.setProjectJson(dto.getProjectJson());
        appointment.setStatus(Appointment.STATUS_WAIT);
        appointment.setActivityDayId(dto.getActivityDayId());
        appointment.setIsSendMsg(dto.getIsSendMsg());
        appointment.setIsArriveSendMsg(dto.getIsArriveSendMsg());
        //标记预约的新增模式
        appointment.setAddMode(dto.getAddMode());
        //多余的等待时间
        appointment.setWaitDuration(dto.getWaitTime());
        //医生治疗时间
        appointment.setDoctorZlDuration(dto.getDoctorZlTime());
        //判断 预约所属顾问是否为空 为空则为预分诊预约,不为空则为普通预约
        if(StringUtils.isEmpty(corpUserId)){
            appointment.setIsPretriage(BaseEntity.YES);
        }
        //周,取当前周一
        appointment.setWeekStr(DateUtil.formatDate(getWeekDate(st)));
        //预约冲突原因
        appointment.setConflictReason(dto.getConflictReason());
        //处理预约状态和是否转疗逻辑
        handleStatusAndIsSwitch(dto.getAddMode(), appointment, shop, employee, false, null);
        //获取设备相关数据
        List<AppointmentProjectDeviceNo> apdnList = new ArrayList<>();
        List<DeviceNoSchedule> dnsList = AppointmentV2Util.handleDeviceTime(dto.getDeviceTimeJson(), apdnList);
        //治疗开始时间
        Calendar ca = Calendar.getInstance();
        ca.setTime(st);
        ca.add(Calendar.MINUTE, duration - appointment.getTreatDuration());
        Date treatSt = ca.getTime();
        //获取出医生的治疗时间记录
        List<DoctorTime> doctorTimeList = null;
        //后台预约时,zlTimeJson没有对应的开始结束时间,所以要特殊处理
        if(dto.getAddMode() == Appointment.ADD_MODE_BACKSTAGE_V2_3){
            List<Integer> doctorZlTime = new ArrayList<>();
            doctorZlTime.add(0);
            net.sf.json.JSONArray newZlTimeJson = new net.sf.json.JSONArray();
            doctorTimeList = AppointmentV2Util.getDoctorZlTime(dto.getZlTimeJson(), treatSt
                    , doctorId, dto.getShopId(), dayStr , appointment.getMonthStr(), doctorZlTime
                    , newZlTimeJson, dnsList);
            appointment.setDoctorZlDuration(doctorZlTime.get(0));
        }else{
            doctorTimeList = AppointmentV2Util.getDoctorZlTime(dto.getZlTimeJson(), dto.getShopId(), doctorId, dayStr, dayStr.substring(0, 6));
        }
        AppointmentInfo appointmentInfo = new AppointmentInfo();
        appointmentInfo.setZlTimeJson(dto.getZlTimeJson());
        appointmentInfo.setDeviceTimeJson(dto.getDeviceTimeJson());
        appointmentInfo.setAutoTimeType(dto.getHandleNum());
        appointmentInfo.setMealsRemark(dto.getMealsRemark());
        if(!StringUtils.isEmpty(dto.getBirthday())){
            appointmentInfo.setBirthday(DateUtil.parseString_2(dto.getBirthday()));
        }
        //填充冗余用户状态
        appointmentInfo.setUserStatus(user.getUserStatus());
        appointmentInfo.setUserStatusCn(UserStatusConstants.getName(user.getUserStatus()));
        appointmentInfo.setUserInternetCorpUserId(user.getInternetCorpUserId());
        appointmentInfo.setUserInternetCorpUserName(CommonQueryUtil.getEmployeeName(user.getInternetCorpUserId(), commonService));
        appointmentInfo.setUserConsultantId(user.getHisCorpUserId());
        appointmentInfo.setUserConsultantName(CommonQueryUtil.getEmployeeName(user.getHisCorpUserId(), commonService));
        appointmentInfo.setUserShopId(user.getShopId());
        appointmentInfo.setUserShopName(CommonQueryUtil.getShopName(user.getShopId(), commonService));
        appointmentInfo.setUserLevel(user.getUserLevel());
        appointmentInfo.setMemberLevelId(user.getMemberLevelId());
        //添加预约
        appointmentV2Service.add(operatorVo, user, appointment, appointmentInfo, apList, apdnList, doctorTimeList, dnsList);
        JSONObject obj = new JSONObject();
        obj.put("id", appointment.getId());
        return Result.success(obj);
    }
    /** 编辑预约 */
    @RequestMapping("/edit")
    public Result edit(@RequestBody AppointmentV2Dto dto) {
        log.info("编辑预约参数:{}", JSONObject.toJSONString(dto));
        //获取操作人信息
        OperatorVo operatorVo = getOperatorInfo(dto);
        if(StringUtils.isEmpty(dto.getId())) {
            throw new TipsException("预约标识为空!");
        }
        if (dto.getAddMode() == null) {
            throw new TipsException("预约方式为空!");
        }
        Appointment appointment = commonService.selectOneByKey(AppointmentMapper.class, dto.getId());
        if(appointment == null) {
            throw new TipsException("查询不到预约信息!");
        }
        appointment.setOrStartTime(appointment.getStartTime());
        if(appointment.getAddMode() == Appointment.ADD_MODE_CRM_SELF){
            throw new TipsException("用户自助预约无法编辑!");
        }
        AppointmentInfo appointmentInfo = appointmentV2Service.selectOneByApptId(appointment.getId());
        if(appointmentInfo == null){
            throw new TipsException("查询不到预约其他信息!");
        }
        User user = commonService.selectOneByKey(UserMapper.class, appointment.getUserId());
        if(user == null){
            throw new TipsException("查询不到预约用户信息!");
        }
        //复制一个旧预约信息对象
        Appointment oldInfo = new Appointment();
        BeanUtils.copyProperties(appointment, oldInfo);
        //顾问或顾问助理,才判断所属顾问id不能为空
        String corpUserId = StringUtils.isEmpty(dto.getCorpUserId()) ? null : dto.getCorpUserId();
        Employee employee = checkEmployee(operatorVo, corpUserId);
        if(oldInfo.getStatus() == Appointment.STATUS_CANCEL) {
            throw new TipsException("已取消的预约不能修改!");
        }
        if(oldInfo.getCancelStatus() == Appointment.CANCEL_STATUS_APPLY) {
            throw new TipsException("申请取消中的预约不能修改!");
        }
        if(oldInfo.getStatus() == Appointment.STATUS_SUC && oldInfo.getIsArrive() == BaseEntity.YES) {
            throw new TipsException("已到店的预约不能修改!");
        }
        if(StringUtils.isEmpty(dto.getStartTime())) {
            throw new TipsException("请选择预约时间!");
        }
        Date st = DateUtil.parseString_1(dto.getStartTime());
        if(st == null) {
            throw new TipsException("请选择预约时间!");
        }
        if(dto.getAppType() == null) {
            throw new TipsException("请选择预约类型!");
        }
        if(dto.getGtTime() == null || dto.getMzTime() == null || dto.getMfTime() == null || dto.getSqTime() == null || dto.getZlTime() == null || dto.getWaitTime() == null
                || dto.getGtTime() < 0 || dto.getMzTime() < 0 || dto.getMfTime() < 0 || dto.getSqTime() < 0 || dto.getZlTime() < 0 || dto.getWaitTime() < 0) {
            throw new TipsException("请填写正确的沟通时间、面诊时间、麻敷时间、术前准备时间、治疗准备开始时间、治疗时间!");
        }
        //自动计算预约时长
        Integer duration = dto.getGtTime() + dto.getMzTime() + dto.getMfTime() + dto.getSqTime() + dto.getZlTime() + dto.getWaitTime();
        //把传过来的时间减去沟通时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(st);
        Calendar ca = Calendar.getInstance();
        ca.setTime(st);
        ca.add(Calendar.MINUTE, duration);
        Date et = ca.getTime();
        if(StringUtils.noNull(dto.getShopId())) {
            appointment.setShopId(dto.getShopId());
        }
        Shop shop = commonService.selectOneByKey(ShopMapper.class, appointment.getShopId());
        if(shop == null){
            throw new TipsException("查询不到预约门店信息!");
        }
        if(dto.getSpecialUser() != null){
            appointment.setSpecialUser(dto.getSpecialUser());
        }
        if(dto.getRealComDuration() != null) {
            appointment.setRealComDuration(dto.getRealComDuration());
        }
        if(dto.getRealDocComDuration() != null) {
            appointment.setRealDocComDuration(dto.getRealDocComDuration());
        }
        if(dto.getRealDuration() != null) {
            appointment.setRealDuration(dto.getRealDuration());
        }
        if(dto.getRealProDuration() != null) {
            appointment.setRealProDuration(dto.getRealProDuration());
        }
        if(dto.getRealTreatDuration() != null) {
            appointment.setRealTreatDuration(dto.getRealTreatDuration());
        }
        if(dto.getRealReadyDuration() != null) {
            appointment.setRealReadyDuration(dto.getRealReadyDuration());
        }
        if(dto.getRealWaitDuration() != null){
            appointment.setRealWaitDuration(dto.getRealWaitDuration());
        }
        if(dto.getRealPalsyDuration() != null){
            appointment.setRealPalsyDuration(dto.getRealPalsyDuration());
        }
        if(dto.getIsArrive() != null) {
            appointment.setIsArrive(dto.getIsArrive());
        }
        if(dto.getIsMicApprove() != null){
            appointment.setIsMicApprove(dto.getIsMicApprove());
        }
        //HIS助手编辑
        if (dto.getAddMode() == Appointment.ADD_MODE_ADVISER_V2_3
                && appointment.getIsMicApprove() == BaseEntity.YES
                && StringUtils.isEmpty(dto.getImgUrls())){
            throw new TipsException("MIC同意必须选择图片!");
        }
        if(StringUtils.noNull(dto.getImgUrls())){
            appointment.setImgUrls(dto.getImgUrls());
        }
        if(dto.getObeyDoctor() != null){
            appointment.setObeyDoctor(dto.getObeyDoctor());
        }
        if(dto.getObeyTime() != null){
            appointment.setObeyTime(dto.getObeyTime());
        }
        if(appointment.getIsArrive() == BaseEntity.YES && oldInfo.getIsArrive() == BaseEntity.NO) {
            if (oldInfo.getStatus() != Appointment.STATUS_SUC
                    || oldInfo.getCancelStatus() == Appointment.CANCEL_STATUS_APPLY) {
                throw new TipsException("该预约非确认状态或者申请取消中!");
            }
            project = commonService.selectOneByKey(ProjectMapper.class, id);
            if(project == null || project.getIsDel() == BaseEntity.YES || project.getIsUp() == BaseEntity.NO)
            {
                 throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到项目或者项目下架");
            }
//            //获取最大的术前准备时间
//            if(project.getReadyDuration() > sqTime){
//                sqTime = project.getReadyDuration();
        }
        if(dto.getIsSwitch() != null) {
            appointment.setIsSwitch(dto.getIsSwitch());
        }
        if(StringUtils.noNull(dto.getVisitType())) {
            appointment.setVisitType(dto.getVisitType());
        }
        if(dto.getIsSendMsg() != null) {
            dto.setIsSendMsg(appointment.getIsSendMsg() != null ? appointment.getIsSendMsg() : BaseEntity.NO);
        }
        if (dto.getIsArriveSendMsg() != null){
            appointment.setIsArriveSendMsg(dto.getIsArriveSendMsg());
        }
        String dayStr = DateUtil.formatDate(st);
        //预约关联项目
        List<Project> projectList = new ArrayList<>();
        List<AppointmentProject> apList = getAppointmentProjectList(dto, projectList);
        //医生限制数量校验
        AppointmentTool.doctorLimitPreNumCheck(dto.getShopId(), appointment.getId(), dto.getDoctorId(), st ,commonService);
        //检测非繁忙项目
        int rulePreType = AppointmentTool.preProjectNonBusy(dto.getShopId(), appointment.getId(),st,projectList,null,commonService);
        //检查是否不占用转疗名额
        Integer occupySwitch = dto.getIsSwitch();
        if(BaseEntity.YES.equals(occupySwitch)){
            occupySwitch = TransferUtil.checkTransferNotOccupy(commonService
                    , appointmentInfo==null?null:appointmentInfo.getUserLevel(), dto.getShopId() , st, dto.getDoctorId(), employee , projectList);
        }
        //获取设备相关数据
        List<AppointmentProjectDeviceNo> apdnList = new ArrayList<>();
        List<DeviceNoSchedule> dnsList = AppointmentV2Util.handleDeviceTime(dto.getDeviceTimeJson(), apdnList);
        //获取出医生的治疗时间记录
        List<DoctorTime> doctorTimeList = null;
        //后台预约时,zlTimeJson没有对应的开始结束时间,所以要特殊处理
        if(dto.getAddMode() == Appointment.ADD_MODE_BACKSTAGE_V2_3){
            List<Integer> doctorZlTime = new ArrayList<>();
            doctorZlTime.add(0);
            net.sf.json.JSONArray newZlTimeJson = new net.sf.json.JSONArray();
            //治疗开始时间
            ca.setTime(st);
            ca.add(Calendar.MINUTE, duration - dto.getZlTime());
            Date treatSt = ca.getTime();
            doctorTimeList = AppointmentV2Util.getDoctorZlTime(dto.getZlTimeJson(), treatSt
                    , dto.getDoctorId(), dto.getShopId(), dayStr , appointment.getMonthStr(), doctorZlTime
                    , newZlTimeJson, dnsList);
            appointment.setDoctorZlDuration(doctorZlTime.get(0));
        }else{
            doctorTimeList = AppointmentV2Util.getDoctorZlTime(dto.getZlTimeJson(), dto.getShopId(), dto.getDoctorId(), dayStr, dayStr.substring(0, 6));
        }
//        if(isChangeTime == 1 || dto.getIsProjectChange() == 1) {
//            String [] projectIdsArr = StringUtils.isEmpty(dto.getProjectIds()) ? new String[0] : dto.getProjectIds().split(",");
//            //重新判断活动日
//            ActivityDay activityDay = getActivityDay(dto.getShopId(), dayStr, dto.getDoctorId(), projectIdsArr);
//            //预约开始时间在活动时间段里面
//            if(activityDay != null && st.compareTo(activityDay.getStartTime()) != -1 && st.compareTo(activityDay.getEndTime()) < 1){
//                appointment.setActivityDayId(activityDay.getId());
//            }else{
//                appointment.setActivityDayId(null);
//            }
            pNumMap.put(id, num);
            //判断治疗时间等于0,则默认5分钟
            pZlMap.put(id, project.getUseDuration() == null ? 0 : project.getUseDuration());
            pMap.put(id, new ProjectVo(project, num, dto.getShopId()));
            pMap.get(id).setZlTime(project.getUseDuration() / 60);
            projectIdsArr[i] = id;
            projectNamesArr[i] = project.getName();
            apList.add(new AppointmentProject(id, num));
            //治疗时间(分钟) = 项目使用时长(秒) / 60
            zlTime += project.getUseDuration() / 60;
//        }
        //赋值对应字段
        appointment.setAddMode(dto.getAddMode());
        appointment.setEditTime(new Date());
        appointment.setDuration(duration);
        appointment.setPalsyDuration(dto.getMfTime());
        appointment.setComDuration(dto.getGtTime());
        appointment.setDocComDuration(dto.getMzTime());
        appointment.setTreatDuration(dto.getZlTime());
        appointment.setReadyDuration(dto.getSqTime());
        appointment.setWaitDuration(dto.getWaitTime());
        appointment.setStartTime(st);
        appointment.setEndTime(et);
        appointment.setDayStr(dayStr);
        appointment.setWeekStr(DateUtil.formatDate(getWeekDate(st)));
        appointment.setIsAnaesthesia(dto.getMfTime() > 0 ? BaseEntity.YES : BaseEntity.NO);
        appointment.setNotifyStatus(Appointment.NOTIFY_STATUS_SUC);
        appointment.setSpecialUser(dto.getSpecialUser());
        appointment.setOccupySwitch(occupySwitch);
        appointment.setRulePreType(rulePreType);
        appointment.setDoctorId(dto.getDoctorId());
        appointment.setNurseId(dto.getNurseId());
        appointment.setCorpUserId(corpUserId);
        appointment.setRemark(dto.getRemark());
        appointment.setProjectIds(dto.getProjectIds());
        appointment.setProjectNames(dto.getProjectNames());
        appointment.setProjectJson(dto.getProjectJson());
        appointment.setComRoomId(dto.getComRoomId());
        appointment.setTreatRoomId(dto.getTreatRoomId());
        appointment.setConflictReason(dto.getConflictReason());
        //如果预约已审核,则记录一下已审核修改
        if(appointment.getStatus() == Appointment.STATUS_SUC || appointment.getStatus() == Appointment.STATUS_FAIL) {
            appointment.setIsModify(BaseEntity.YES);
        }
        //预约其他信息
        appointmentInfo.setAutoTimeType(dto.getHandleNum());
        appointmentInfo.setZlTimeJson(dto.getZlTimeJson());
        appointmentInfo.setDeviceTimeJson(dto.getDeviceTimeJson());
        appointmentInfo.setMealsRemark(dto.getMealsRemark());
        if(!StringUtils.isEmpty(dto.getBirthday())){
            appointmentInfo.setBirthday(DateUtil.parseString_2(dto.getBirthday()));
        }
        //处理预约状态和是否转疗逻辑
        handleStatusAndIsSwitch(dto.getAddMode(), appointment, shop, employee, true, oldInfo);
        appointmentV2Service.edit(operatorVo, user, oldInfo, appointment, appointmentInfo, apList, apdnList, doctorTimeList, dnsList);
        return Result.success();
    }
    /** 取消预约 */
    @RequestMapping("/cancel")
    public Result cancel(@RequestBody AppointmentV2Dto dto){
        log.info("取消预约参数:{}", JSONObject.toJSONString(dto));
        OperatorVo operatorVo = getOperatorInfo(dto);
        if(StringUtils.isEmpty(dto.getId())) {
            throwParamException("预约标识为空!");
        }
//        if(StringUtils.isEmpty(dto.getAnnexUrl())) {
//            throwParamException("对话凭证不能为空!");
//        }
        Appointment appointment = commonService.selectOneByKey(AppointmentMapper.class, dto.getId());
        if(appointment == null) {
            throwParamException("查询不到预约信息!");
        }
        if(appointment.getStatus() == Appointment.STATUS_FAIL || appointment.getStatus() == Appointment.STATUS_CANCEL) {
            throwServiceException("预约不成功或者已取消!");
        }
        if(appointment.getCancelStatus() != Appointment.CANCEL_STATUS_NONE && appointment.getCancelStatus() != Appointment.CANCEL_STATUS_FAIL) {
            throwServiceException("取消状态不正确!");
        }
        //判断用户自助的预约,操作人非用户时,不能取消
        if(appointment.getAddMode() == Appointment.ADD_MODE_CRM_SELF
                && !operatorVo.getOpId().equals(appointment.getCreateManId())){
            throw new TipsException("无法取消用户的自助预约!");
        }
        Integer isSendMsg = dto.getIsSendMsg();
        if(isSendMsg == null) {
            isSendMsg = appointment.getIsSendMsg() != null ? appointment.getIsSendMsg() : BaseEntity.NO;
        }
        appointmentV2Service.cancel(appointment, operatorVo, dto.getRemark(), isSendMsg, dto.getAnnexUrl());
        return Result.success();
    }
    ///////////////////////////////////////////////////////////////////////////
    /** 获取操作人信息 */
    private OperatorVo getOperatorInfo(AppointmentV2Dto dto){
        if(dto.getOpType() == null){
            throw new TipsException("操作人类型为空!");
        }
        if(StringUtils.isEmpty(dto.getOpId())){
            throw new TipsException("操作人标识为空!");
        }
        OperatorVo operatorVo = null;
        if(dto.getOpType() == OperatorConstants.OP_TYPE_EMPLOYEE){
            if(StringUtils.isEmpty(dto.getOpRoleId())){
                throw new TipsException("操作人角色标识为空!");
            }
            Employee employee = employeeRoleService.selectEmployeeById(dto.getOpRoleId());
            if(employee == null){
                throw new TipsException("查询不到员工操作人信息!");
            }
            operatorVo = new OperatorVo(employee);
        }else if(dto.getOpType() == OperatorConstants.OP_TYPE_USER){
            User user = commonService.selectOneByKey(UserMapper.class, dto.getOpId());
            if(user == null){
                throw new TipsException("查询不到用户操作人信息!");
            }
            operatorVo = new OperatorVo(user);
        } else {
            throw new TipsException("操作人类型错误!");
        }
        return operatorVo;
    }
    //校验员工判断
    private Employee checkEmployee(OperatorVo operatorVo, String corpUserId){
        if(operatorVo.getOpType() == OperatorConstants.OP_TYPE_EMPLOYEE){
            //顾问或顾问助理,才判断所属顾问id不能为空
//            if(RoleType.UNIQUE_STR_ADVISER_ASSISTANT.equals(operatorVo.getRoleStr())
//                    || RoleType.UNIQUE_STR_ADVISER_LEADER.equals(operatorVo.getRoleStr()) ){
//                if(StringUtils.isEmpty(corpUserId)){
//                    throw new TipsException("请选择预约所属顾问!");
//                }
//            }
            if(operatorVo.getEmployee() == null){
                throw new TipsException("预约操作员工信息为空[020]");
            }
            return operatorVo.getEmployee();
        }
        return null;
    }
    //获取预约项目
    private List<AppointmentProject> getAppointmentProjectList(AppointmentV2Dto dto,  List<Project> projectList){
        //预约关联项目
        Project project;
        AppointmentProject appointmentProject = null;
        List<AppointmentProject> apList = new ArrayList<>();
        String[] projectIdArr = StringUtils.isEmpty(dto.getProjectIds()) ? new String[0] : dto.getProjectIds().split(",");
        //复查时可不选预约项目
        if(StringUtils.isEmpty(dto.getProjectIds(), dto.getProjectJson()) || projectIdArr == null || projectIdArr.length <= 0) {
            if(dto.getAppType() != Appointment.APP_TYPE_REVIEW){
                throw new TipsException("请选择项目!");
            }
        }else{
            JSONArray arr = JSONArray.parseArray(dto.getProjectJson());
            JSONObject json;
            for(int i = 0; i < arr.size(); i++){
                json = arr.getJSONObject(i);
                //查找预约项目
                project = commonService.selectOneByKey(ProjectMapper.class, json.getString("id"));
                if(project == null || project.getIsDel().equals(BaseEntity.YES) || project.getIsUp().equals(BaseEntity.NO)) {
                    throw new TipsException("查询不到项目或者项目已下架!");
                }
                projectList.add(project);
                appointmentProject = new AppointmentProject(json);
                apList.add(appointmentProject);
            }
        }
        return apList;
    }
    //处理预约状态和是否转疗逻辑
    private void handleStatusAndIsSwitch(Integer addMode, Appointment appointment, Shop shop, Employee employee, boolean isEdit, Appointment oldInfo) {
        //默认状态:待审核
        appointment.setStatus(Appointment.STATUS_WAIT);
        //CRM预约的默认状态:预约成功
        if(addMode == Appointment.ADD_MODE_CRM_SELF){
            appointment.setStatus(Appointment.STATUS_SUC);
            return;
        }
        //要MIC同意的则跳过
        if(appointment.getIsMicApprove() == BaseEntity.YES){
            return;
        }
        //非HIS助手顾问新增、后台信息的
        if(addMode != Appointment.ADD_MODE_ADVISER_V2_3
                && addMode != Appointment.ADD_MODE_BACKSTAGE_V2_3
                && addMode != Appointment.ADD_MODE_BACKSTAGE_V2_4){
            return;
        }
        //判断系统是否设置了自动审核成功
        SystemParameter systemParameter = systemParameterService.selectOneByName(SystemParameter.KEY_APP_AUTO_EXAMINE_SUC);
        if(systemParameter == null || !"1".equals(systemParameter.getParamValue())){
           return;
        }
        appointment.setStatus(Appointment.STATUS_SUC);
        //判断是否会占用转疗,不占用跳过
        if(BaseEntity.NO.equals(appointment.getOccupySwitch())){
            return;
        }
        if(!isEdit){
            //新增预约判断:预约门店和预约员工门店不一致,判断门店是否还有转疗数量
            if(!appointment.getShopId().equals(employee.getShopId())){
                int countIsSwitch = appointmentV2Service.selectCountIsSwitch(appointment.getDayStr(), shop.getId());
                log.info("新增预约:{}{}转疗单数:{},门店设置最大的转疗数:{}", shop.getName(), appointment.getDayStr(), countIsSwitch, shop.getMaxSwitchNum());
                if(shop !=null  && shop.getMaxSwitchNum()!=null && countIsSwitch>=shop.getMaxSwitchNum()){
                    throw new TipsException("转疗订单已经超过门店设置的最大转疗订单,转疗订单数:"+countIsSwitch+",门店转疗最大数:"+shop.getMaxSwitchNum());
                }
                appointment.setIsSwitch(BaseEntity.YES);
            }
        }else{
            //编辑预约判断:预约门店和预约员工门店不一致,判断门店是否还有转疗数量
            if(!appointment.getShopId().equals(employee.getShopId()) && !oldInfo.getDayStr().equals(appointment.getDayStr())){
                int countIsSwitch= appointmentV2Service.selectCountIsSwitch(appointment.getDayStr(), appointment.getShopId());
                log.info("编辑预约:{}{}转疗预约数:{},门店设置最大的转疗数:{}", shop.getName(), appointment.getDayStr(), countIsSwitch, shop.getMaxSwitchNum());
                if(shop !=null  && shop.getMaxSwitchNum() != null && countIsSwitch >= shop.getMaxSwitchNum()){
                    throw new TipsException("转疗订单已经超过门店设置的最大转疗订单,转疗订单数:"+countIsSwitch+",门店转疗最大数:"+shop.getMaxSwitchNum());
                }
                appointment.setIsSwitch(BaseEntity.YES);
            }
        }
    }
    //活动日
    public ActivityDay getActivityDay(String shopId, String dayStr, String doctorId, String [] projectIdsArr){
        Map<String, Object> map = new HashMap<>();
        SqlSentence sqlSentence = new SqlSentence();
        //查询当天是否有活动日
        map.put("isDel", BaseEntity.NO);
        map.put("shopId", shopId);
        map.put("dayStr", dayStr);
        sqlSentence.setM(map);
        sqlSentence.setSqlSentence("select * from activity_day where isDel = #{m.isDel} and shopId = #{m.shopId} and dayStr = #{m.dayStr}");
        ActivityDay activityDay = commonService.selectOne(ActivityDayMapper.class, sqlSentence);
        if(activityDay == null){
            return activityDay;
        }
        //判断选择医生是否符合
        boolean isDoctor = true;
        //类型为指定医生时
        if(activityDay.getType() == ActivityDay.TYPE_APPOINT_DOCTOR){
            isDoctor = activityDay.getDoctorIds().indexOf(doctorId) != -1;
        }
        boolean isProject = true;
        if(isDoctor == true){
            //判断选择项目是否符合
            for(String projectId : projectIdsArr){
                //某个项目不符合时,直接返回false
                if(activityDay.getProjectIds().indexOf(projectId) == -1){
                    isProject = false;
                    break;
                }
            }
        }
        boolean isOk = true;
        if(isDoctor && isProject){
            sqlSentence.setSqlSentence("isDel = #{m.isDel} and shopId = #{m.shopId} and activityDayId = #{m.activityDayId} and status = #{m.status} ");
            map.put("status", Appointment.STATUS_SUC);
            map.put("activityDayId", activityDay.getId());
            int count = commonService.selectCount(AppointmentMapper.class, sqlSentence);
            //判断预约人数是否超过可接待人数
            if(count >= activityDay.getNumber()){
                isOk = false;
            }
        }else{
            //都不符合时提示或者下一天
            isOk = false;
        }
        //符合条件,且在活动日时间段时,关联活动日id
        if(isOk == false){
            activityDay = null;
        }
        return activityDay;
    }
    //获取周日期
    private Date getWeekDate(Date st){
        //周,取当前周一
        Calendar ca = Calendar.getInstance();
        ca.setTime(st);
        int dayOfWeek = ca.get(Calendar.DAY_OF_WEEK);
        if(dayOfWeek == 1) {
            ca.add(Calendar.DAY_OF_YEAR, -6);
        }else{
            ca.add(Calendar.DAY_OF_YEAR, 2 - dayOfWeek);
        }
        return ca.getTime();
    }
    //检测用户创建预约
    public void checkUserAdd(Integer addMode, String doctorId, Date startTime, Date endTime){
        //非CRM自助预约跳过
        if(addMode != Appointment.ADD_MODE_CRM_SELF){
            return;
        }
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        AppointmentInfo appointmentInfo = new AppointmentInfo();
        if(appointment == null){
            appointment = new Appointment();
        }
        appointment.setStatus(Appointment.STATUS_SUC);
        appointment.setSourceId(dto.getSourceId());
        appointment.setShopId(dto.getShopId());
        appointment.setDoctorId(dto.getDoctorId());
        appointment.setUserId(dto.getUserId());
        //项目信息
        appointment.setProjectJson(arr.toJSONString());
        appointment.setProjectIds(org.apache.commons.lang.StringUtils.join(projectIdsArr,","));
        appointment.setProjectNames(org.apache.commons.lang.StringUtils.join(projectNamesArr,","));
        //时间信息
        appointment.setStartTime(DateUtil.parseString_1(dto.getStartTime()));
        appointment.setEndTime(DateUtil.parseString_1(dto.getEndTime()));
        appointment.setDayStr(DateUtil.formatDate(appointment.getStartTime()));
        //周字符串
        Calendar ca = Calendar.getInstance();
        ca.setTime(appointment.getStartTime());
        int dayOfWeek = ca.get(Calendar.DAY_OF_WEEK);
        if(dayOfWeek == 1)
        {
            ca.add(Calendar.DAY_OF_YEAR, -6);
        }else{
            ca.add(Calendar.DAY_OF_YEAR, 2 - dayOfWeek);
        }
        appointment.setWeekStr(DateUtil.formatDate(ca.getTime()));
        appointment.setMonthStr(appointment.getDayStr().substring(0, 6));
        //所属顾问,使用用户所属顾问
        appointment.setCorpUserId(user.getHisCorpUserId());
        //创建人信息
        appointment.setCreateManId(user.getId());
        appointment.setCreateManName(user.getName());
        appointment.setCreateManType(Appointment.CREATE_MAN_TYPE_USER);
        //默认就诊类型:医美
        appointment.setVisitType("医美");
        //备注
        appointment.setRemark(dto.getRemark());
        //新增模式
        appointment.setAddMode(Appointment.ADD_MODE_CRM_CREATE);
        //
        appointment.setComDuration(0);
        appointment.setDocComDuration(0);
        appointment.setReadyDuration(0);
        appointment.setProDuration(0);
        appointment.setPalsyDuration(0);
        appointment.setTreatDuration(zlTime);
        appointment.setDoctorZlDuration(zlTime);
        //总时长 = 面诊 + 沟通 + 敷麻 + 术前 + 准备等待 + 治疗
        int duration = appointment.getDocComDuration() + appointment.getComDuration() + appointment.getPalsyDuration() + appointment.getReadyDuration() + appointment.getWaitDuration() + appointment.getTreatDuration();
        appointment.setDuration(duration);
        //获取出医生的治疗时间记录
        JSONArray zlTimeJson = AppAutoMateV5Util.handleDoctorZlTime(pMap, appointment.getStartTime());
        appointmentInfo.setZlTimeJson(zlTimeJson.toString());
        List<DoctorTime> doctorTimeList = AppointmentV2Util.getDoctorZlTime(zlTimeJson.toString(), appointment.getShopId(), appointment.getDoctorId(), appointment.getDayStr(), appointment.getMonthStr());
        //新增时
        if(StringUtils.isEmpty(appointment.getId())){
            //冗余用户信息
            appointmentInfo.setUserStatus(user.getUserStatus());
            appointmentInfo.setUserStatusCn(UserStatusConstants.getName(user.getUserStatus()));
            appointmentInfo.setUserInternetCorpUserId(user.getInternetCorpUserId());
            appointmentInfo.setUserInternetCorpUserName(CommonQueryUtil.getEmployeeName(user.getInternetCorpUserId(), commonService));
            appointmentInfo.setUserConsultantId(user.getHisCorpUserId());
            appointmentInfo.setUserConsultantName(CommonQueryUtil.getEmployeeName(user.getHisCorpUserId(), commonService));
            appointmentInfo.setUserShopId(user.getShopId());
            appointmentInfo.setUserShopName(CommonQueryUtil.getShopName(user.getShopId(), commonService));
            appointmentInfo.setUserLevel(user.getUserLevel());
            appointmentInfo.setMemberLevelId(user.getMemberLevelId());
            //根据用户角色信息,获取对应可使用预约类型,多个取第一个
            net.sf.json.JSONArray appTypeArr = AppointmentListUtil.userToAppType(dto.getUserId(), commonService);
            if(appTypeArr == null || appTypeArr.size() < 1){
                 throw new PlatTipsException(PlatformCode.ERROR_TIPS, "没有对应预约类型!");
            }
            appointment.setAppType(appTypeArr.getInt(0));
        values.put("doctorId", doctorId);
        values.put("startTime", DateUtil.formatDate_2(startTime));
        values.put("endTime", DateUtil.formatDate_2(endTime));
        sqlSentence.setSqlSentence(" select count(1) from appointment where isDel = 0 and status = 1 and startTime = #{m.startTime} and endTime = #{m.endTime} ");
        int count = commonService.selectCountSql(AppointmentMapper.class, sqlSentence);
        if(count > 0){
            throw new TipsException("非常抱歉!\n" + DateUtil.formatDate(startTime, "MM月dd HH:mm - ") + DateUtil.formatDate(endTime, "HH:mm") + " 预约时间已被预约\n请返回重新选择时间。");
        }
        //添加预约
        if(dto.getHandleType() == 1){
            appointmentService.crmAdd(appointment, apList, doctorTimeList, null, appointmentInfo);
        }else if(dto.getHandleType() == 2){
            appointmentService.crmEdit(appointment, appointmentInfo, 0, doctorTimeList, apList, null, null);
        }
        JSONObject data = new JSONObject();
        data.put("appointmentId", appointment.getId());
        return Result.success(data);
    }
    //
//    /** CRM预约(CRM的再升美学预约) */
//    @RequestMapping("/crm/app")
//    public Result crmApp(@RequestBody(required = false) AppointmentCrmAddDto dto) {
//
//        if (dto == null) {
//             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "请求参数为空!");
//        }
//
//        if(dto.getHandleType() == null){
//             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "处理类型不能为空!");
//        }
//
//        Appointment appointment = null;
//        //判断处理类型处理类型(1新增2编辑3取消)
//        if(dto.getHandleType() == 2 || dto.getHandleType() == 3){
//            if(StringUtils.isEmpty(dto.getAppointmentId())){
//                throw new PlatTipsException(PlatformCode.ERROR_TIPS, "预约标识不能为空!");
//            }
//
//            appointment = commonService.selectOneByKey(AppointmentMapper.class, dto.getAppointmentId());
//            if(appointment == null){
//                throw new PlatTipsException(PlatformCode.ERROR_TIPS, "查询不到预约信息!");
//            }
//
//            //如果处理类型为取消,则直接调用取消逻辑然后返回
//            if(dto.getHandleType() == 3){
//                appointmentService.cancel(appointment, dto.getRemark(), Appointment.CANCEL_TYPE_CRM_USER, null);
//                return Result.success();
//            }
//        }
//
//        if(StringUtils.isEmpty(dto.getUserId())){
//             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "用户标识为空!");
//        }
//
//        if(StringUtils.isEmpty(dto.getShopId())){
//             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "门店标识为空!");
//        }
//
//        if(StringUtils.isEmpty(dto.getDoctorId())){
//             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "医生标识为空!");
//        }
//
//        if(StringUtils.isEmpty(dto.getStartTime())){
//             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "开始时间为空!");
//        }
//
//        User user = commonService.selectOneByKeyBlob(UserMapper.class, dto.getUserId());
//        if (user == null) {
//             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到用户信息!");
//        }
//
//        Shop shop = commonService.selectOneByKeyBlob(ShopMapper.class, dto.getShopId());
//        if (shop == null) {
//             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到门店信息!");
//        }
//
//        Employee employee = commonService.selectOneByKeyBlob(EmployeeMapper.class, dto.getDoctorId());
//        if (employee == null) {
//            throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到医生信息!");
//        }
//
//        //目前写死一个项目
//        SystemParameter sp = systemParameterService.selectOneByName(SystemParameter.KEY_CRM_APPOINTMENT_PROJECT_ID);
//        if(sp == null || StringUtils.isEmpty(sp.getParamValue())){
//            throw new PlatTipsException(PlatformCode.ERROR_TIPS, "还未配置项目信息!");
//        }
//
//        Project project = commonService.selectOneByKey(ProjectMapper.class, sp.getParamValue());
//        if(project == null){
//             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到项目信息!");
//        }
//
//        if(project == null || project.getIsDel() == BaseEntity.YES || project.getIsUp() == BaseEntity.NO)
//        {
//             throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到项目或者项目下架!");
//        }
//
//        //组装json数组
//        JSONArray arr = new JSONArray();
//        JSONObject temp = new JSONObject();
//        temp.put("id", project.getId());
//        temp.put("num", 1);
//        temp.put("isLifeBeauty", project.getIsLifeBeauty());
//        temp.put("name", project.getName());
//        temp.put("palsyDuration", project.getPalsyDuration());
//        temp.put("isPalsy", project.getPalsyDuration() > 0 ? 1 : 0);
//        temp.put("useDuration", project.getUseDuration());
//        arr.add(temp);
//
//        String [] projectIdsArr =  new String[arr.size()];
//        String [] projectNamesArr = new String[arr.size()];
//        Map<String, Integer> pNumMap = new HashMap<>();
//        Map<String, Integer> pZlMap = new HashMap<>();
//        String id = null;
//        int num = 0;
//        int zlTime = 0;
//
//
//        List<AppointmentProject> apList = new ArrayList<>();
//        LinkedHashMap<String, ProjectVo> pMap = new LinkedHashMap<>();
//        for(int i = 0; i < arr.size(); i ++)
//        {
//            temp = arr.getJSONObject(i);
//            id = temp.getString("id");
//            num = temp.getIntValue("num");
//            if(StringUtils.isEmpty(id) || num <= 0)
//            {
//                 throw new PlatTipsException(PlatformCode.ERROR_TIPS, "请选择正确项目和数量");
//            }
//            project = commonService.selectOneByKey(ProjectMapper.class, id);
//            if(project == null || project.getIsDel() == BaseEntity.YES || project.getIsUp() == BaseEntity.NO)
//            {
//                 throw new PlatTipsException(PlatformCode.ERROR_TIPS, "找不到项目或者项目下架");
//            }
////            //获取最大的术前准备时间
////            if(project.getReadyDuration() > sqTime){
////                sqTime = project.getReadyDuration();
////            }
//            pNumMap.put(id, num);
//            //判断治疗时间等于0,则默认5分钟
//            pZlMap.put(id, project.getUseDuration() == null ? 0 : project.getUseDuration());
//            pMap.put(id, new ProjectVo(project, num, dto.getShopId()));
//            pMap.get(id).setZlTime(project.getUseDuration() / 60);
//            projectIdsArr[i] = id;
//            projectNamesArr[i] = project.getName();
//            apList.add(new AppointmentProject(id, num));
//            //治疗时间(分钟) = 项目使用时长(秒) / 60
//            zlTime += project.getUseDuration() / 60;
//        }
//
//        SqlSentence sqlSentence = new SqlSentence();
//        Map<String, Object> values = new HashMap<>();
//        sqlSentence.setM(values);
//
//        AppointmentInfo appointmentInfo = new AppointmentInfo();
//        if(appointment == null){
//            appointment = new Appointment();
//        }
//        appointment.setStatus(Appointment.STATUS_SUC);
//        appointment.setSourceId(dto.getSourceId());
//        appointment.setShopId(dto.getShopId());
//        appointment.setDoctorId(dto.getDoctorId());
//        appointment.setUserId(dto.getUserId());
//        //项目信息
//        appointment.setProjectJson(arr.toJSONString());
//        appointment.setProjectIds(org.apache.commons.lang.StringUtils.join(projectIdsArr,","));
//        appointment.setProjectNames(org.apache.commons.lang.StringUtils.join(projectNamesArr,","));
//        //时间信息
//        appointment.setStartTime(DateUtil.parseString_1(dto.getStartTime()));
//        appointment.setEndTime(DateUtil.parseString_1(dto.getEndTime()));
//        appointment.setDayStr(DateUtil.formatDate(appointment.getStartTime()));
//        //周字符串
//        Calendar ca = Calendar.getInstance();
//        ca.setTime(appointment.getStartTime());
//        int dayOfWeek = ca.get(Calendar.DAY_OF_WEEK);
//        if(dayOfWeek == 1)
//        {
//            ca.add(Calendar.DAY_OF_YEAR, -6);
//        }else{
//            ca.add(Calendar.DAY_OF_YEAR, 2 - dayOfWeek);
//        }
//        appointment.setWeekStr(DateUtil.formatDate(ca.getTime()));
//        appointment.setMonthStr(appointment.getDayStr().substring(0, 6));
//        //所属顾问,使用用户所属顾问
//        appointment.setCorpUserId(user.getHisCorpUserId());
//        //创建人信息
//        appointment.setCreateManId(user.getId());
//        appointment.setCreateManName(user.getName());
//        appointment.setCreateManType(Appointment.CREATE_MAN_TYPE_USER);
//        //默认就诊类型:医美
//        appointment.setVisitType("医美");
//        //备注
//        appointment.setRemark(dto.getRemark());
//        //新增模式
//        appointment.setAddMode(Appointment.ADD_MODE_CRM_CREATE);
//
//        //
//        appointment.setComDuration(0);
//        appointment.setDocComDuration(0);
//        appointment.setReadyDuration(0);
//        appointment.setProDuration(0);
//        appointment.setPalsyDuration(0);
//        appointment.setTreatDuration(zlTime);
//        appointment.setDoctorZlDuration(zlTime);
//
//        //总时长 = 面诊 + 沟通 + 敷麻 + 术前 + 准备等待 + 治疗
//        int duration = appointment.getDocComDuration() + appointment.getComDuration() + appointment.getPalsyDuration() + appointment.getReadyDuration() + appointment.getWaitDuration() + appointment.getTreatDuration();
//        appointment.setDuration(duration);
//
//        //获取出医生的治疗时间记录
//        JSONArray zlTimeJson = AppAutoMateV5Util.handleDoctorZlTime(pMap, appointment.getStartTime());
//        appointmentInfo.setZlTimeJson(zlTimeJson.toString());
//        List<DoctorTime> doctorTimeList = AppointmentV2Util.getDoctorZlTime(zlTimeJson.toString(), appointment.getShopId(), appointment.getDoctorId(), appointment.getDayStr(), appointment.getMonthStr());
//
//
//        //新增时
//        if(StringUtils.isEmpty(appointment.getId())){
//
//            //冗余用户信息
//            appointmentInfo.setUserStatus(user.getUserStatus());
//            appointmentInfo.setUserStatusCn(UserStatusConstants.getName(user.getUserStatus()));
//            appointmentInfo.setUserInternetCorpUserId(user.getInternetCorpUserId());
//            appointmentInfo.setUserInternetCorpUserName(CommonQueryUtil.getEmployeeName(user.getInternetCorpUserId(), commonService));
//            appointmentInfo.setUserConsultantId(user.getHisCorpUserId());
//            appointmentInfo.setUserConsultantName(CommonQueryUtil.getEmployeeName(user.getHisCorpUserId(), commonService));
//            appointmentInfo.setUserShopId(user.getShopId());
//            appointmentInfo.setUserShopName(CommonQueryUtil.getShopName(user.getShopId(), commonService));
//            appointmentInfo.setUserLevel(user.getUserLevel());
//            appointmentInfo.setMemberLevelId(user.getMemberLevelId());
//
//            //根据用户角色信息,获取对应可使用预约类型,多个取第一个
//            net.sf.json.JSONArray appTypeArr = AppointmentListUtil.userToAppType(dto.getUserId(), commonService);
//            if(appTypeArr == null || appTypeArr.size() < 1){
//                 throw new PlatTipsException(PlatformCode.ERROR_TIPS, "没有对应预约类型!");
//            }
//            appointment.setAppType(appTypeArr.getInt(0));
//        }
//
//        //添加预约
//        if(dto.getHandleType() == 1){
//            appointmentService.crmAdd(appointment, apList, doctorTimeList, null, appointmentInfo);
//        }else if(dto.getHandleType() == 2){
//            appointmentService.crmEdit(appointment, appointmentInfo, 0, doctorTimeList, apList, null, null);
//        }
//
//
//        JSONObject data = new JSONObject();
//        data.put("appointmentId", appointment.getId());
//
//        return Result.success(data);
//    }
}
phi_platform_user/src/main/java/com/hx/phip/controller/appointment/CrmSelfController.java
New file
@@ -0,0 +1,398 @@
package com.hx.phip.controller.appointment;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hx.common.BaseController;
import com.hx.exception.TipsException;
import com.hx.guide.util.CommonQueryUtil;
import com.hx.mybatisTool.SqlSentence;
import com.hx.phiappt.common.OperatorConstants;
import com.hx.phiappt.constants.enums.GroupTypeEnum;
import com.hx.phiappt.dao.mapper.*;
import com.hx.phiappt.model.*;
import com.hx.phiappt.model.user.UserCard;
import com.hx.phip.config.GlobalExceptionHandler;
import com.hx.phip.tool.user.UserCardTool;
import com.hx.resultTool.Result;
import com.hx.util.DateUtil;
import com.hx.util.StringUtils;
import com.hz.his.dto.appointment.AppointmentAutoMateDto;
import com.hz.his.dto.user.UserDto;
import com.hz.his.vo.user.card.UserCardEquityVo;
import com.hz.his.vo.user.card.UserCardItemVo;
import com.hz.his.vo.user.card.UserCardVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.*;
/**
 * CRM自助预约
 * @USER: fhx
 * @DATE: 2024/4/3
 **/
@Slf4j
@RestController
@RequestMapping("/appointment/crm/self")
public class CrmSelfController extends BaseController {
    /** VIP等级字符 */
    public static final String VIP_STR = "金卡|钻石卡|黑卡";
    /** 门店标识(静安店) */
    public static final List<String> SHOP_ID_LIST = Arrays.asList("bb4e8a7d620d11ebb06bb8599f4cafbe");
    @Resource
    private AppAutoMateController appAutoMateController;
    /** 预约页面数据 */
    @RequestMapping("/page/data")
    public Result pageData(@RequestBody UserDto dto){
        JSONObject data = handleUserData(dto.getUserId());
        return Result.success(data);
    }
    /** CRM首页预约列表 */
    @RequestMapping("/index/app/list")
    public Result indexAppList(@RequestBody UserDto dto){
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        //查询用户最近一条预约信息
        values.put("userId", dto.getUserId());
        values.put("status1", Appointment.STATUS_SUC);
        values.put("addMode10", Appointment.ADD_MODE_CRM_SELF);
        StringBuilder sql = new StringBuilder();
        sql.append(" select a.id, a.startTime, a.endTime , s.name as shopName , d.cnName as doctorName, a.doctorId, a.isArrive, a.projectJson ")
                .append(" from appointment a ")
                .append(" left join shop s on s.id = a.shopId ")
                .append(" left join employee d on d.id = a.doctorId ")
                .append(" where a.isDel = 0  and  a.userId = #{m.userId} and a.startTime > now() ")
                .append(" and a.status = #{m.status1} and a.addMode = #{m.addMode10} ")
                .append(" order by a.startTime asc limit 1 ");
        sqlSentence.setSqlSentence(sql.toString());
        List<Map<String, Object>> list = commonService.selectListMap(AppointmentMapper.class, sqlSentence);
        if(list != null && list.size() > 0){
            JSONArray arr;
            StringBuilder nameSb = new StringBuilder();
            for(Map<String, Object> m : list){
                nameSb.setLength(0);
                arr = JSONArray.parseArray(m.get("projectJson").toString());
                if(arr ==null || arr.size() < 1){
                    continue;
                }
                for(int i = 0 ; i < arr.size(); i++){
                    if(nameSb.length() > 0){
                        nameSb.append(",");
                    }
                    nameSb.append(arr.getJSONObject(i).getString("commonName"));
                }
                m.remove("projectJson");
                m.put("projectNames", nameSb.toString());
            }
        }
        JSONObject data = new JSONObject();
        data.put("list", list);
        return Result.success(data);
    }
    /** 用户预约列表 */
    @RequestMapping("/list")
    public Result selfList(@RequestBody UserDto dto){
        if(StringUtils.isEmpty(dto.getUserId())){
            throw new TipsException("用户标识为空!");
        }
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        values.put("userId", dto.getUserId());
        values.put("status1", Appointment.STATUS_SUC);
        values.put("addMode9", Appointment.ADD_MODE_CRM_CREATE);
        StringBuilder sql = new StringBuilder();
        sql.append(" select a.id, a.startTime, a.endTime, s.name as shopName , d.cnName as doctorName, a.doctorId ")
                .append(" , a.status, a.isArrive, a.projectJson, a.addMode ")
                .append(" from appointment a ")
                .append(" left join shop s on s.id = a.shopId ")
                .append(" left join employee d on d.id = a.doctorId ")
                .append(" where a.isDel = 0 and a.userId = #{m.userId}  ")
                .append(" and a.addMode != #{m.addMode9} ")
                .append(" order by a.startTime desc ");
        sqlSentence.setSqlSentence(sql.toString());
        //分页插件
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        List<Map<String, Object>> list = commonService.selectListMap(AppointmentMapper.class, sqlSentence);
        PageInfo<Map<String, Object>> pageInfo = new PageInfo<>(list);
        Map<String, Object> data = new HashMap<>();
        data.put("list", pageInfo.getList());
        data.put("pageNum", pageInfo.getPageNum());
        data.put("pageSize", pageInfo.getPageSize());
        data.put("pages", pageInfo.getPages());
        data.put("total", pageInfo.getTotal());
        data.put("isLastPage", pageInfo.isIsLastPage());
        return Result.success(data);
    }
    /** 用户自助预约项目 */
    @RequestMapping("/project/list")
    public Result projectList(@RequestBody UserDto dto){
        if(StringUtils.isEmpty(dto.getUserId())){
            throw new TipsException("用户标识为空!");
        }
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        values.put("userId", dto.getUserId());
        StringBuilder sql = new StringBuilder();
        //查询用户项目
        sql.append(" SELECT '1' as commonType, ui.id as commonId, ui.goodsId as commonSubId, ui.goodsName as commonName ")
                .append(" , p.id as projectId, p.name as projectName, p.unit, p.specification ")
                .append(" , p.isLifeBeauty,p.useDuration, p.palsyDuration ")
                .append(" , spu.id as spuId, spu.name as spuName, pi.departmentCode, pi.departmentName ")
                .append(" FROM user_project_item ui ")
                .append(" join project_type_relation tr on tr.isDel = 0 and tr.skuId = ui.goodsId and tr.moduleType = 2 ")
                .append(" JOIN project_assoc_sku pas ON pas.skuId = ui.goodsId ")
                .append(" JOIN project p ON p.id = pas.projectId  ")
                .append(" join project_general spu on spu.id = p.projectGeneralId ")
                .append(" join project_info pi on pi.projectId = pas.skuId and pi.departmentCode is not null ")
                .append(" WHERE ui.notUsedNum > 0 AND ui.isDel = 0 AND ui.effectiveStatus = 1 ")
                .append(" and ui.userId = #{m.userId} AND ui.goodsType = 'project' AND p.type = 1 ")
                .append(" group by ui.id ");
        sqlSentence.setSqlSentence(sql.toString());
        List<Map<String, Object>> userProjectList = commonService.selectListMap(UserProjectItemMapper.class, sqlSentence);
        //查询用户卡项下的项目
        sqlSentence.setSqlSentence(" select * from user_card where isDel = 0 and userId = #{m.userId} and effectiveStatus = 1 ");
        List<UserCard> userCardList = commonService.selectList(UserCardMapper.class, sqlSentence);
//        log.info("用户卡项数据:{}", JSONObject.toJSONString(userCardList));
        List<UserCardVo> userCardVoList = new ArrayList<>();
        //遍历查询用户卡项条目是否可用
        for(UserCard userCard : userCardList){
            userCardVoList.add(UserCardTool.getUserCardSurplus(userCard,commonService));
        }
        sql.setLength(0);
        sql.append(" SELECT '2' as commonType, i.id as commonSubId, i.commonName ")
                .append(" , p.id as projectId,  p.name as projectName, p.unit, p.specification ")
                .append(" , p.isLifeBeauty, p.useDuration, p.palsyDuration ")
                .append(" , spu.id as spuId, spu.name as spuName, pi.departmentCode, pi.departmentName ")
                .append(" FROM card_item_info i ")
                //基于项目映射表查对应打卡项目的卡项类型,而不是到sku(commonId)
                .append(" join project_type_relation tr on tr.isDel = 0 and tr.skuId = i.cardItemId and tr.moduleType = 2 ")
                .append(" JOIN project_assoc_sku pas ON pas.skuId = i.commonId ")
                .append(" JOIN project p ON p.id = pas.projectId  ")
                .append(" join project_general spu on spu.id = p.projectGeneralId ")
                .append(" join project_info pi on pi.projectId = pas.skuId and pi.departmentCode is not null ")
                .append(" WHERE i.isDel = 0 and i.commonType = 'project' and i.id = #{m.id} ")
                .append(" GROUP by i.id ");
        String userCardSql = sql.toString();
        sqlSentence.setSqlSentence(userCardSql);
        JSONArray arr = new JSONArray();
        JSONArray groupArr = null;
        JSONObject json;
        Map<String, Object> map;
        List<Map<String, Object>> cardProjectList;
        //先遍历用户卡项
//        log.info("卡项vo数据:{}", JSONObject.toJSONString(userCardVoList));
        for(UserCardVo userCardVo : userCardVoList){
            //再遍历卡项权益
            groupArr = new JSONArray();
            for(UserCardEquityVo userCardEquityVo : userCardVo.getUserCardEquityVoList()) {
                cardProjectList = new ArrayList<>();
                //再遍历卡项权益下对应的条目
                for(UserCardItemVo userCardItemVo : userCardEquityVo.getUserCardItemVoList()){
                    //非项目类型的条目跳过
                    if(!GroupTypeEnum.PROJECT.getCode().equals(userCardItemVo.getItemType())){
                        continue;
                    }
                    values.put("id", userCardItemVo.getItemId());
                    map = commonService.selectOneMap(CardItemInfoMapper.class, sqlSentence);
                    if(map == null || map.size() < 1){
                        continue;
                    }
                    map.put("deductionNum", userCardItemVo.getDeductionNum()); //总抵扣次数
                    map.put("deductionSingle", userCardItemVo.getDeductionSingle()); //单次抵扣次数
                    map.put("usedNum", userCardItemVo.getUsedNum()); //已抵扣次数
                    map.put("surplusNum", userCardItemVo.getSurplusNum()); //剩余可抵扣总次数
                    cardProjectList.add(map);
                }
                //卡项权益没有查询项目的跳过
                if(cardProjectList.size() == 0){
                    continue;
                }
                json = new JSONObject();
                json.put("equityName", userCardEquityVo.getEquityName());
                json.put("equityType", userCardEquityVo.getEquityType());
                json.put("conditionValue", userCardEquityVo.getConditionValue()); //条件值
                json.put("projectList", cardProjectList);
                groupArr.add(json);
            }
            //卡项权益分组不为空
            if(groupArr.size() > 0){
                json = new JSONObject();
                json.put("commonId", userCardVo.getUserCardId());
                json.put("cardName", userCardVo.getCardName());
                json.put("groupList", groupArr);
                arr.add(json);
            }
        }
        json = new JSONObject();
        json.put("projectList", userProjectList);
        json.put("cardList", arr);
        json.put("notDeptTips", "当前项目无科室医生,请联系您的专属顾问进行预约!");
        return Result.success(json);
    }
    /** 自助预约时间 */
    @RequestMapping("/getTime")
    public Result getTime(@RequestBody AppointmentAutoMateDto dto){
        if(StringUtils.isEmpty(dto.getUserId())){
            throw new TipsException("预约用户标识为空!");
        }
        if(StringUtils.isEmpty(dto.getShopId())){
            throw new TipsException("预约门店标识为空!");
        }
        if(StringUtils.isEmpty(dto.getDoctorId())){
            throw new TipsException("预约医生标识为空!");
        }
        if(StringUtils.isEmpty(dto.getArriveDate())){
            throw new TipsException("预约日期为空!");
        }
        if(StringUtils.isEmpty(dto.getProjectJson())){
            throw new TipsException("预约项目为空!");
        }
        dto.setOpType(OperatorConstants.OP_TYPE_USER);
        dto.setOpId(dto.getUserId());
        dto.setVisitType("医美");
        dto.setAppType(Appointment.APP_TYPE_TREATMENT);
        dto.setStartTime("09:00");
        dto.setDaySpace(true);
        Result result;
        try{
            result = appAutoMateController.addApplyBland(dto);
        }catch (Exception e){
            log.info("获取用户自助预约时间失败:{}", GlobalExceptionHandler.getExceptionInformation(e));
            throw new TipsException("获取预约时间失败!");
        }
        return result;
    }
    /** 获取一周内医生排班 */
    @RequestMapping("/get/doctor/time")
    public Result getDoctorTime(@RequestBody AppointmentAutoMateDto dto){
        if(StringUtils.isEmpty(dto.getShopId())){
            throw new TipsException("预约门店标识为空!");
        }
        if(StringUtils.isEmpty(dto.getDoctorId())){
            throw new TipsException("预约医生标识为空!");
        }
        Date now = new Date();
        //当天时间
        String startDay = DateUtil.formatDate(now);
        //一周后结束时间
        String endDay = DateUtil.formatDate(DateUtil.addDay(now, 6));
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        values.put("shopId", dto.getShopId());
        values.put("doctorId", dto.getDoctorId());
        values.put("startDay", startDay);
        values.put("endDay", endDay);
        values.put("timeType", DoctorTime.TIME_TYPE_WORK);
        StringBuffer sql = new StringBuffer();
        sql.append(" select DATE_FORMAT(startTime, '%Y-%m-%d') as dateTime ")
                .append(" from doctor_Time ")
                .append(" where isDel = 0 and doctorId = #{m.doctorId} ")
                .append(" and shopId = #{m.shopId} and timeType = #{m.timeType} ")
                .append(" and dayTime >= #{m.startDay} and dayTime <= #{m.endDay} ");
        sqlSentence.setSqlSentence(sql.toString());
        List<Map<String, Object>> list = commonService.selectListMap(DoctorTimeMapper.class, sqlSentence);
        JSONObject data = new JSONObject();
        data.put("list", list);
        return Result.success(data);
    }
    ////////////////////////////////////////////////////////////////////////////////////////
    private JSONObject handleUserData(String userId){
        if(StringUtils.isEmpty(userId)){
            throw new TipsException("用户标识为空!");
        }
        User user = commonService.selectOneByKey(UserMapper.class, userId);
        if(user == null){
            throw new TipsException("查询不到用户信息!");
        }
        JSONObject data = new JSONObject();
        //是否能预约:0否1是,默认0
        data.put("isCanApp", BaseEntity.NO);
        //预约门店 = 用户所属门店
        data.put("shopId", user.getShopId());
        data.put("shopName", CommonQueryUtil.getShopName(user.getShopId(), commonService));
        //直接跳过:非VIP客户
        if(!VIP_STR.contains(user.getUserLevel())){
            data.put("notAppType", 1);
            return data;
        }
        //直接跳过:客户所属门店非规定的
        if(!SHOP_ID_LIST.contains(user.getShopId())){
            data.put("notAppType", 2);
            return data;
        }
        //对应条件成立,则用户能预约
        data.put("isCanApp", BaseEntity.YES);
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        values.put("userId", userId);
        //查询用科室关系数量
        sqlSentence.setSqlSentence(" select * from user_families_room where isDel = 0 and userId = #{m.userId} ");
        List<UserFamiliesRoom> userFamiliesRoomList = commonService.selectList(UserFamiliesRoomMapper.class, sqlSentence);
        //用户科室关系为空则跳过
        if(userFamiliesRoomList  == null && userFamiliesRoomList.size() < 1){
            data.put("notAppType", 3);
            return data;
        }
        data.put("userFamiliesRoomList", userFamiliesRoomList);
        return data;
    }
}
phi_platform_user/src/main/java/com/hx/phip/controller/appointment/CrmSelfV2Controller.java
New file
@@ -0,0 +1,438 @@
package com.hx.phip.controller.appointment;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hx.common.BaseController;
import com.hx.exception.TipsException;
import com.hx.mybatisTool.SqlSentence;
import com.hx.phiappt.common.OperatorConstants;
import com.hx.phiappt.constants.tool.user.UserTool;
import com.hx.phiappt.dao.mapper.*;
import com.hx.phiappt.model.*;
import com.hx.phip.config.GlobalExceptionHandler;
import com.hx.resultTool.Result;
import com.hx.util.DateUtil;
import com.hx.util.StringUtils;
import com.hz.his.dto.appointment.AppointmentAutoMateDto;
import com.hz.his.dto.appointment.AppointmentDto;
import com.hz.his.dto.appointment.AppointmentV2Dto;
import com.hz.his.dto.user.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * CRM自助预约v2
 * 按phis医生排班和预约项目进行预约
 * @USER: fhx
 * @DATE: 2024/4/3
 **/
@Slf4j
@RestController
@RequestMapping("/appointment/crm/self/v2")
public class CrmSelfV2Controller extends BaseController {
    @Resource
    private AppAutoMateController appAutoMateController;
    @Resource
    private AppointmentController appointmentController;
    public static final String FORMAT = "yyyy-MM-ddHH:mm";
    /** 预约门店 */
    @RequestMapping("/shop/list")
    public Result shopList(){
        SqlSentence sqlSentence = new SqlSentence();
        sqlSentence.setSqlSentence(" select id, name, workTime from shop where isDel = 0 and isUp = 1 ");
        List<Map<String, Object>> list = commonService.selectListMap(ShopMapper.class, sqlSentence);
        Map<String, Object> data = new HashMap<>();
        data.put("list", list);
        return Result.success(data);
    }
    /** 医生排班 */
    @RequestMapping("/doctor/time/list")
    public Result doctorTimeList(@RequestBody AppointmentAutoMateDto dto){
        if(StringUtils.isEmpty(dto.getShopId())){
            throw new TipsException("门店标识为空!");
        }
        if(StringUtils.isEmpty(dto.getStartTime())){
            throw new TipsException("开始时间为空!");
        }
        if(StringUtils.isEmpty(dto.getEndTime())){
            throw new TipsException("结束时间为空!");
        }
//        //版本1返回
//        return Result.success(doctorTimeListV1(dto));
        //版本2返回
        return Result.success(doctorTimeListV2(dto));
    }
    /** 预约项目SPU */
    @RequestMapping("/spu/list")
    public Result spuList(){
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        values.put("type", ProjectType.TYPE_APPOINTMENT);
        sqlSentence.setSqlSentence(" select id, name from project_general where isDel = 0 and type = #{m.type} and isUp = 1 order by orderNum asc ");
        List<Map<String, Object>> list = commonService.selectListMap(ProjectGeneralMapper.class, sqlSentence);
        Map<String, Object> data = new HashMap<>();
        data.put("list", list);
        return Result.success(data);
    }
    /** 预约项目 */
    @RequestMapping("/project/list")
    public Result projectList(@RequestBody AppointmentDto dto){
        if(dto.getPageSize() == null || dto.getPageSize() > 20){
            dto.setPageSize(20);
        }
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        values.put("type", ProjectType.TYPE_APPOINTMENT);
        StringBuilder sql = new StringBuilder();
        sql.append(" select p.id, p.name, p.isLifeBeauty, p.unit, p.specification  ")
                .append(", p.useDuration / 60 as useDuration ")
                .append(", p.palsyDuration / 60 as palsyDuration ")
                .append(", (p.useDuration + p.palsyDuration) / 60 as duration ")
//                .append(", (p.useDuration + p.palsyDuration + p.intervalDuration + p.readyDuration) / 60 as duration")
                .append(" from project p ")
                .append(" where p.isDel = 0 and p.isUp = 1 and p.isShow = 1 ")
                .append(" and p.type = #{m.type} ");
        if(StringUtils.noNull(dto.getKeyWord())){
            sql.append(" and p.name like '%").append(dto.getKeyWord()).append("%' ");
        }
        //spuId查询
        if(StringUtils.noNull(dto.getSpuId())){
            sql.append(" and p.projectGeneralId = '").append(dto.getSpuId()).append("' ");
        }
        sql.append(" order by p.orderNum asc ");
        sqlSentence.setSqlSentence(sql.toString());
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        List<Map<String, Object>> list = commonService.selectListMap(ProjectMapper.class, sqlSentence);
        PageInfo<Map<String, Object>> pageInfo = new PageInfo<>(list);
        Map<String, Object> data = new HashMap<>();
        data.put("list", pageInfo.getList());
        data.put("pageNum", pageInfo.getPageNum());
        data.put("pageSize", pageInfo.getPageSize());
        data.put("pages", pageInfo.getPages());
        data.put("total", pageInfo.getTotal());
        data.put("isLastPage", pageInfo.isIsLastPage());
        return Result.success(data);
    }
    //////////////////////////////////////////////////////////////////////////////////////////
    /** 自助预约匹配时间 */
    @RequestMapping("/mate/time")
    public Result mateTime(@RequestBody AppointmentAutoMateDto dto){
        if(StringUtils.isEmpty(dto.getUserId())){
            throw new TipsException("预约用户标识为空!");
        }
        if(StringUtils.isEmpty(dto.getShopId())){
            throw new TipsException("预约门店标识为空!");
        }
        if(StringUtils.isEmpty(dto.getDoctorId())){
            throw new TipsException("预约医生标识为空!");
        }
        if(StringUtils.isEmpty(dto.getArriveDate())){
            throw new TipsException("预约日期为空!");
        }
        if(StringUtils.isEmpty(dto.getStartTime())){
            throw new TipsException("预约到店时间为空!");
        }
        if(StringUtils.isEmpty(dto.getProjectJson())){
            throw new TipsException("预约项目为空!");
        }
        Date startTime = DateUtil.parseString(dto.getArriveDate() + dto.getStartTime(), FORMAT);
        if(startTime == null){
            throw new TipsException("预约到店时间错误!");
        }
        //查询用户是否有到店记录
        dto.setOpType(OperatorConstants.OP_TYPE_USER);
        dto.setOpId(dto.getUserId());
        dto.setVisitType("医美");
        //判断用户没到店过,则初诊
        if(UserTool.getUserArrivalNum(commonService, dto.getUserId()) == 0){
            dto.setAppType(Appointment.APP_TYPE_FIRST);
        }else{
            dto.setAppType(Appointment.APP_TYPE_TREATMENT);
        }
        dto.setMateNum(3);
//        dto.setDaySpace(true);
        Result result;
        try{
            result = appAutoMateController.addApplyBland(dto);
        }catch (Exception e){
            log.info("获取用户自助预约时间失败:{}", GlobalExceptionHandler.getExceptionInformation(e));
            throw new TipsException("获取预约时间失败!");
        }
        //匹配报错提示文案
        JSONObject data = result.getJsonObject(result.getData());
        JSONArray arr = data.getJSONArray("list");
        if(arr != null && arr.size() > 0){
            data.remove("errMsg");
        }
        if(StringUtils.noNull(data.getString("errMsg")) ){
            throw new TipsException(data.getString("errMsg"));
        }
        return result;
    }
    /** 新增预约 */
    @RequestMapping("/add")
    public Result add(@RequestBody AppointmentV2Dto dto){
        if(StringUtils.isEmpty(dto.getUserId())){
            throw new TipsException("用户标识为空!");
        }
        dto.setOpId(dto.getUserId());
        dto.setOpType(OperatorConstants.OP_TYPE_USER); //操作人类型:用户
        dto.setAddMode(Appointment.ADD_MODE_CRM_SELF); //crm自助预约
        dto.setVisitType("医美");
        //判断用户没到店过,则初诊
        if(UserTool.getUserArrivalNum(commonService, dto.getUserId()) == 0){
            dto.setAppType(Appointment.APP_TYPE_FIRST);
        }else{
            dto.setAppType(Appointment.APP_TYPE_TREATMENT);
        }
        dto.setIsMicApprove(BaseEntity.NO); //无需MIC同意
        dto.setIsSendMsg(BaseEntity.YES); //发送短信通知
        dto.setIsArriveSendMsg(BaseEntity.YES); //发送短信通知
        //新增预约
        Result result = appointmentController.add(dto);
        if(!result.checkCode()){
            log.error("新增用户自助预约V2失败:{}", JSONObject.toJSONString(result));
            throw new TipsException("新增预约失败!");
        }
        JSONObject data = result.getJsonObject(result.getData());
        //查询返回对应预约信息
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        values.put("id", data.getString("id"));
        StringBuilder sql = new StringBuilder();
        sql.append(" select a.id, a.startTime, a.endTime , d.cnName as doctorName, a.doctorId ")
                .append(" , a.status, a.isArrive, a.projectNames, a.projectJson, a.addMode, a.createManName, a.createManType, remark ")
                .append(" , s.name as shopName, s.province, s.city, s.area, s.addr")
                .append(" from appointment a ")
                .append(" left join shop s on s.id = a.shopId ")
                .append(" left join employee d on d.id = a.doctorId ")
                .append(" where a.isDel = 0 and a.id = #{m.id}  ");
        sqlSentence.setSqlSentence(sql.toString());
        Map<String, Object> dataMap = commonService.selectOneMap(AppointmentMapper.class, sqlSentence);
        return Result.success(dataMap);
    }
    /** 取消预约 */
    @RequestMapping("/cancel")
    public Result cancel(@RequestBody AppointmentV2Dto dto){
        if(StringUtils.isEmpty(dto.getUserId())){
            throw new TipsException("用户标识为空!");
        }
        dto.setOpId(dto.getUserId());
        dto.setOpType(OperatorConstants.OP_TYPE_USER);
        return appointmentController.cancel(dto);
    }
    /** 用户预约列表 */
    @RequestMapping("/list")
    public Result list(@RequestBody UserDto dto){
        if(StringUtils.isEmpty(dto.getUserId())){
            throw new TipsException("用户标识为空!");
        }
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        values.put("userId", dto.getUserId());
        values.put("addMode9", Appointment.ADD_MODE_CRM_CREATE);
        StringBuilder sql = new StringBuilder();
        sql.append(" select a.id, a.startTime, a.endTime, d.cnName as doctorName, a.doctorId, a.addMode ")
                .append(" , a.status, a.isArrive, a.projectJson, a.addMode, a.createManName, a.createManType, a.remark ")
                .append(" , s.name as shopName, s.province, s.city, s.area, s.addr")
                .append(" from appointment a ")
                .append(" left join shop s on s.id = a.shopId ")
                .append(" left join employee d on d.id = a.doctorId ")
                .append(" where a.isDel = 0 and a.userId = #{m.userId}  ")
                //只查询预约成功和取消的
                .append(" and a.status in (").append(Appointment.STATUS_SUC).append(",").append(Appointment.STATUS_CANCEL).append(") ")
//                .append(" and a.addMode != #{m.addMode9} ")
                .append(" order by a.startTime desc ");
        sqlSentence.setSqlSentence(sql.toString());
        //分页插件
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        List<Map<String, Object>> list = commonService.selectListMap(AppointmentMapper.class, sqlSentence);
        PageInfo<Map<String, Object>> pageInfo = new PageInfo<>(list);
        Map<String, Object> data = new HashMap<>();
        data.put("list", pageInfo.getList());
        data.put("pageNum", pageInfo.getPageNum());
        data.put("pageSize", pageInfo.getPageSize());
        data.put("pages", pageInfo.getPages());
        data.put("total", pageInfo.getTotal());
        data.put("isLastPage", pageInfo.isIsLastPage());
        return Result.success(data);
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
//    private void checkTime(Date startTime){
//        Calendar calendar = Calendar.getInstance();
//        calendar.setTime(startTime);
//        int hour = calendar.get(Calendar.HOUR);
//        if(hour ){}
//    }
    /** 医生排班(按日期选医生) */
    private Map<String, Object> doctorTimeListV1(AppointmentAutoMateDto dto){
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        values.put("timeType", DoctorTime.TIME_TYPE_WORK);
        values.put("shopId", dto.getShopId());
        values.put("startTime", dto.getStartTime());
        values.put("endTime", dto.getEndTime());
        StringBuilder sql = new StringBuilder();
        sql.append(" select dt.doctorId, e.cnName as doctorName, e.imgUrl ")
                .append(" , date_format(dt.startTime, '%Y-%m-%d') as startTime ")
                .append(" from doctor_time dt ")
                .append(" left join employee e on e.id = dt.doctorId ")
                .append(" where dt.isDel = 0 and dt.timeType = #{m.timeType} and dt.shopId = #{m.shopId} ")
                .append(" and dt.dayTime >= #{m.startTime} and dt.endTime <= #{m.endTime} ")
                .append(" order by dt.dayTime asc ");
        sqlSentence.setSqlSentence(sql.toString());
        List<Map<String, Object>> list = commonService.selectListMap(DoctorTimeMapper.class, sqlSentence);
        if(list == null || list.size() < 1){
            return null;
        }
        //先遍历按日期归类
        List<Map<String, Object>> dataList;
        TreeMap<Object, List<Map<String, Object>>> treeMap = new TreeMap<>();
        for(Map<String, Object> map : list){
            dataList = treeMap.get(map.get("startTime"));
            if(!treeMap.containsKey(map.get("startTime"))){
                dataList = new ArrayList<>();
                treeMap.put(map.get("startTime"), dataList);
            }
            dataList.add(map);
        }
        //然后再组装
        JSONArray arr = new JSONArray();
        JSONObject json;
        for(Map.Entry<Object, List<Map<String, Object>>> entry : treeMap.entrySet()){
            json = new JSONObject();
            json.put("dateTime", entry.getKey());
            json.put("doctorList", entry.getValue());
            arr.add(json);
        }
        json = new JSONObject();
        json.put("list", arr);
        json.put("version", "1");
        return json;
    }
    /** 医生排班(按医生选日期) */
    private Map<String, Object> doctorTimeListV2(AppointmentAutoMateDto dto){
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        values.put("shopId", dto.getShopId());
        StringBuilder sql = new StringBuilder();
        //查询门店下的医生员工
        sql.append(" select e.id as doctorId, e.cnName as doctorName, e.imgUrl ")
                .append(" from employee_role r ")
                .append(" join employee e on e.id = r.employeeId ")
                .append(" where r.isDel = 0 and e.isDel = 0 and e.isJob = 1 ")
                .append(" and r.shopId = #{m.shopId} and r.roleUniqueStr = #{m.roleUniqueStr} ");
        sqlSentence.setSqlSentence(sql.toString());
        List<Map<String, Object>> doctorList = commonService.selectListMap(EmployeeMapper.class, sqlSentence);
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("list", doctorList);
        dataMap.put("version", "2");
        if(doctorList == null || doctorList.size() < 1){
            return dataMap;
        }
        //查询门店下对应日期范围的医生排班
        values.put("timeType", DoctorTime.TIME_TYPE_WORK);
        values.put("startTime", dto.getStartTime());
        values.put("endTime", dto.getEndTime());
        sql.setLength(0);
        sql.append(" select dt.doctorId, date_format(dt.startTime, '%Y-%m-%d') as startTime ")
                .append(" from doctor_time dt ")
                .append(" left join employee e on e.id = dt.doctorId ")
                .append(" where dt.isDel = 0 and dt.timeType = #{m.timeType} and dt.shopId = #{m.shopId} ")
                .append(" and dt.dayTime >= #{m.startTime} and dt.endTime <= #{m.endTime} ")
                .append(" order by dt.dayTime asc ");
        sqlSentence.setSqlSentence(sql.toString());
        List<Map<String, Object>> doctorTimeList = commonService.selectListMap(DoctorTimeMapper.class, sqlSentence);
        if(doctorTimeList == null || doctorTimeList.size() < 1){
            return dataMap;
        }
        //按医生id分组归类排班数据
        Map<String, List<Map<String, Object>>> doctorTimeMap = doctorTimeList.stream().collect(Collectors.groupingBy(m->m.get("doctorId").toString()));
        //先遍历按日期归类
        List<Map<String, Object>> dateList;
        for(Map<String, Object> doctorMap : doctorList){
            dateList = doctorTimeMap.get(doctorMap.get("doctorId"));
            if(dateList == null || dateList.size() < 1){
                continue;
            }
            //按时间排序
            dateList.sort(Comparator.comparing((Map<String, Object> h) -> ((String) h.get("startTime"))));
            dataMap.put("dateList", dateList);
        }
        return dataMap;
    }
}
phi_platform_user/src/main/java/com/hx/phip/service/EmployeeService.java
@@ -34,4 +34,7 @@
    /** 查询角色信息 by id */
    EmployeeRole selectRoleInfoById(String id);
    /** 查询员工 By id and role */
    Employee selectOneByIdAndRole(String id, String roleUniqueStr);
}
phi_platform_user/src/main/java/com/hx/phip/service/appointment/AppointmentV2Service.java
New file
@@ -0,0 +1,40 @@
package com.hx.phip.service.appointment;
import com.hx.phiappt.model.*;
import com.hx.phiappt.vo.OperatorVo;
import java.util.List;
/**
 * @USER: fhx
 * @DATE: 2024/4/1
 **/
public interface AppointmentV2Service {
    /** 新增预约 */
    void add(OperatorVo operatorVo, User user, Appointment appointment, AppointmentInfo appointmentInfo
            , List<AppointmentProject> appointmentProjectList
            , List<AppointmentProjectDeviceNo> appointmentProjectDeviceNoList
            , List<DoctorTime> doctorTimeList
            , List<DeviceNoSchedule> deviceNoScheduleList);
    /** 编辑预约 */
    void edit(OperatorVo operatorVo, User user, Appointment oldInfo, Appointment appointment
            , AppointmentInfo appointmentInfo
            , List<AppointmentProject> appointmentProjectList
            , List<AppointmentProjectDeviceNo> appointmentProjectDeviceNoList
            , List<DoctorTime> doctorTimeList
            , List<DeviceNoSchedule> deviceNoScheduleList);
    /** 取消预约  */
    void cancel(Appointment appointment, OperatorVo operatorVo, String remark, Integer isSendMsg, String annexUrl);
    /** 查询当天门店转疗订单总数数据 */
    int selectCountIsSwitch(String dayStr, String shopId);
    /** 查询预约其他信息 */
    AppointmentInfo selectOneByApptId(String apptId);
}
phi_platform_user/src/main/java/com/hx/phip/service/appointment/impl/AppointmentV2ServiceImpl.java
New file
@@ -0,0 +1,617 @@
package com.hx.phip.service.appointment.impl;
import com.hx.common.dao.CommonDao;
import com.hx.common.service.CommonService;
import com.hx.exception.TipsException;
import com.hx.mybatisTool.SqlSentence;
import com.hx.phiappt.constants.tool.appointment.AppointmentTool;
import com.hx.phiappt.constants.tool.appointment.GuideSyncAppItemUtil;
import com.hx.phiappt.dao.mapper.*;
import com.hx.phiappt.dao.mapper.AppointmentV2Mapper;
import com.hx.phiappt.model.*;
import com.hx.phiappt.util.AppointmentV2Util;
import com.hx.phiappt.util.system.config.AppointmentConfigUtil;
import com.hx.phiappt.vo.OperatorVo;
import com.hx.phip.service.appointment.AppointmentV2Service;
import com.hx.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
/**
 * @USER: fhx
 * @DATE: 2024/4/1
 **/
@Slf4j
@Transactional
@Service
public class AppointmentV2ServiceImpl implements AppointmentV2Service {
    @Resource
    private CommonDao commonDao;
    @Resource
    private CommonService commonService;
    @Resource
    private AppointmentV2Mapper appointmentV2Mapper;
    /** 新增预约 */
    @Override
    public void add(OperatorVo operatorVo, User user, Appointment appointment, AppointmentInfo appointmentInfo
            , List<AppointmentProject> appointmentProjectList
            , List<AppointmentProjectDeviceNo> appointmentProjectDeviceNoList
            , List<DoctorTime> doctorTimeList
            , List<DeviceNoSchedule> deviceNoScheduleList)
    {
        //检测判断项目是否护士生美
        checkProjectIsShengMei(appointment, false);
        //新增预约
        if(commonDao.insert(AppointmentMapper.class, appointment) != 1){
            throw new TipsException("新增预约失败!");
        }
        //预约信息-关联表
        appointmentInfo.setApptId(appointment.getId());
        if(commonDao.insert(AppointmentInfoMapper.class, appointmentInfo) != 1){
            throw new TipsException("新增预约信息失败!");
        }
        //处理预约涉及的其他数据
        handleAppointmentOtherData(false, appointment, doctorTimeList, appointmentProjectList, appointmentProjectDeviceNoList, deviceNoScheduleList);
        //预约日志
        StringBuffer logRemark = new StringBuffer();
        logRemark.append("(").append(Appointment.addModeStr(appointment.getAddMode())).append(")新增预约");
        String content = logRemark.toString();
        logRemark.setLength(0);
        logRemark.append("预约项目:").append(appointment.getProjectNames());
        commonDao.insert(AppointmentLogMapper.class, new AppointmentLog(operatorVo, appointment.getId(), content, logRemark.toString()));
        //HIS助手预约时
        if(appointment.getAddMode() == Appointment.ADD_MODE_ADVISER_V2_3){
            //新增系统消息通知
            commonDao.insert(SystemMessageMapper.class, new SystemMessage(SystemMessage.TYPE_CORP_USER_ADD_APP, appointment.getId(), operatorVo));
        }
        //处理预约后定时项逻辑
        handleTimerHandleItem(appointment);
        //更新用户最后预约医生和预约顾问
        StringBuilder sql = new StringBuilder();
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        if(StringUtils.noNull(appointment.getDoctorId()) && !appointment.getDoctorId().equals(user.getLastDoctorId())){
            values.put("lastDoctorId", appointment.getDoctorId());
            sql.append(" lastDoctorId = #{m.lastDoctorId} ");
        }
        if(StringUtils.noNull(appointment.getCorpUserId()) && !appointment.getCorpUserId().equals(user.getLastCorpUserId())){
            values.put("lastCorpUserId", appointment.getCorpUserId());
            sql.append(sql.length() > 0 ? "," : "").append(" lastCorpUserId = #{m.lastCorpUserId} ");
        }
        if(sql.length() > 0){
            values.put("userId", user.getId());
            sql.append(" where id = #{m.userId} ");
            sqlSentence.setSqlSentence(sql.toString());
            commonDao.updateWhere(UserMapper.class, sqlSentence);
        }
        //用户预约备注
        UserAppointmentRemark userAppointmentRemark = appointmentV2Mapper.selectUserAppointmentRemarkByUserId(user.getId());
        if(userAppointmentRemark == null) {
            userAppointmentRemark = new UserAppointmentRemark();
            userAppointmentRemark.setRemark(appointment.getRemark());
            userAppointmentRemark.setUserId(user.getId());
            commonDao.insert(UserAppointmentRemarkMapper.class, userAppointmentRemark);
        }else{
            userAppointmentRemark.setRemark(appointment.getRemark());
            commonDao.updateAll(UserAppointmentRemarkMapper.class, userAppointmentRemark);
        }
    }
    /** 编辑预约 */
    @Override
    public void edit(OperatorVo operatorVo, User user, Appointment oldInfo, Appointment appointment
            , AppointmentInfo appointmentInfo
            , List<AppointmentProject> appointmentProjectList
            , List<AppointmentProjectDeviceNo> appointmentProjectDeviceNoList
            , List<DoctorTime> doctorTimeList
            , List<DeviceNoSchedule> deviceNoScheduleList)
    {
        //检测判断项目是否护士生美
        checkProjectIsShengMei(appointment, false);
        //更新预约
        if( appointmentV2Mapper.updateAppointment(appointment) != 1){
            throw new TipsException("编辑更新预约失败!");
        }
        //更新预约信息-关联表
        if(appointmentV2Mapper.updateAppointmentInfo(appointmentInfo) != 1){
            throw new TipsException("更新预约信息失败!");
        }
        //处理预约涉及的其他数据
        handleAppointmentOtherData(true, appointment, doctorTimeList, appointmentProjectList, appointmentProjectDeviceNoList, deviceNoScheduleList);
        //HIS助手预约时
        if(appointment.getAddMode() == Appointment.ADD_MODE_ADVISER_V2_3){
            //新增系统消息通知
            commonDao.insert(SystemMessageMapper.class, new SystemMessage(SystemMessage.TYPE_CORP_USER_EDIT_APP, appointment.getId(), operatorVo));
        }
        //获取变化日志内容
        StringBuffer logInfo = new StringBuffer();
        logInfo.append("(").append(Appointment.addModeStr(appointment.getAddMode())).append(")编辑预约");
        String logRemark = AppointmentV2Util.checkChangeByEdit(oldInfo, appointment, commonService);
        commonDao.insert(AppointmentLogMapper.class, new AppointmentLog(operatorVo, appointment.getId(), logInfo.toString(), logRemark));
        //统计门店转疗数量
        editUpdateShopSwitchNum(oldInfo, appointment, oldInfo.getStatus() == Appointment.STATUS_FAIL ? 1 : 0);
        //更新下预约成功和待审核数量的统计
        editUpdateAppNumCul(oldInfo, appointment);
        TimerHandleItem timerHandleItem = null;
        if(appointment.getStatus() == Appointment.STATUS_SUC){
            //判断编辑前状态非成功,则统计成功数量
            if(oldInfo.getStatus() != Appointment.STATUS_SUC){
                timerHandleItem = new TimerHandleItem();
                timerHandleItem.setItemNum(1);
                timerHandleItem.setItemStr(appointment.getId());
                timerHandleItem.setItemStr1(appointment.getShopId());
                timerHandleItem.setType(TimerHandleItem.TYPE_APP_APPLY_SUC);
                timerHandleItem.setCreateTime(appointment.getStartTime());
                commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
            }
            if(appointment.getIsSendMsg() != null && appointment.getIsSendMsg() == 1){
                //发送短信 暂时不发送 0303
                timerHandleItem = new TimerHandleItem();
                timerHandleItem.setItemNum(3);
                timerHandleItem.setItemStr(appointment.getId());
                timerHandleItem.setType(TimerHandleItem.TYPE_SEND_SHORT_MSG);
                commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
            }
            //同步到导医
            GuideSyncAppItemUtil.syncApp(commonService, appointment.getId());
        }else {
            //同步到导医
            GuideSyncAppItemUtil.cancle(commonService, appointment.getId());
        }
    }
    /** 取消预约  */
    @Override
    public void cancel(Appointment appointment, OperatorVo operatorVo, String remark, Integer isSendMsg, String annexUrl) {
        //修改状态
        SqlSentence sqlSentence = new SqlSentence();
        sqlSentence.setSqlSentence(" cancelRemark = #{m.cancelRemark}, notifyStatus = #{m.notifyStatus}, status = #{m.status}, cancelStatus = #{m.cancelStatus}, cancelType = #{m.cancelType}, isSendMsg = #{m.isSendMsg}, cancelTime = now() where id = #{m.id}");
        Map<String, Object> map = new HashMap<>();
        map.put("notifyStatus", Appointment.NOTIFY_STATUS_NONE);
        map.put("status", Appointment.STATUS_CANCEL);
        map.put("cancelStatus", Appointment.CANCEL_STATUS_SUC);
        map.put("cancelType", Appointment.CANCEL_TYPE_CORP_USER);
        map.put("cancelRemark", remark);
        map.put("id", appointment.getId());
        map.put("isSendMsg", isSendMsg);
        sqlSentence.setM(map);
        if(commonDao.updateWhere(AppointmentMapper.class, sqlSentence) != 1){
            throw new TipsException("取消预约失败!");
        }
        if (!StringUtils.isEmpty(annexUrl)){
            //更新info表图片
            AppointmentTool.updateAppointmentInfo(appointment.getId(), annexUrl, commonDao);
        }
        StringBuffer logInfo = new StringBuffer();
        logInfo.append("(").append(Appointment.addModeStr(appointment.getAddMode())).append(")取消预约");
        String content = logInfo.toString();
        logInfo.setLength(0);
        logInfo.append("取消预约");
        if(StringUtils.noNull(remark)){
            logInfo.append(",取消备注:").append(remark);
        }
        commonDao.insert(AppointmentLogMapper.class, new AppointmentLog(operatorVo, appointment.getId(), content, logInfo.toString()));
        //移除时间相关
        AppointmentV2Util.removeRelateTime(appointment, commonService);
        TimerHandleItem timerHandleItem = null;
        //更新下预约统计
        if(appointment.getStatus() == Appointment.STATUS_WAIT || appointment.getStatus() == Appointment.STATUS_SUC) {
            timerHandleItem = new TimerHandleItem();
            timerHandleItem.setItemStr(appointment.getId());
            timerHandleItem.setItemNum(-1);
            timerHandleItem.setCreateTime(appointment.getStartTime());
            timerHandleItem.setItemStr1(appointment.getShopId());
            if (appointment.getStatus() == Appointment.STATUS_WAIT) {
                //待审核预约统计-1
                timerHandleItem.setType(TimerHandleItem.TYPE_APP_APPLY);
            } else if (appointment.getStatus() == Appointment.STATUS_SUC) {
                //审核成功统计-1
                timerHandleItem.setType(TimerHandleItem.TYPE_APP_SUC);
            }
            commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
        }
        //转疗数量统计-1
        if(appointment.getIsSwitch() == BaseEntity.YES) {
            timerHandleItem = new TimerHandleItem();
            timerHandleItem.setItemStr(appointment.getId());
            timerHandleItem.setItemNum(-1);
            timerHandleItem.setCreateTime(appointment.getStartTime());
            timerHandleItem.setItemStr1(appointment.getShopId());
            timerHandleItem.setType(TimerHandleItem.TYPE_APP_SWITCH);
            commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
        }
        //发送系统消息
        timerHandleItem = new TimerHandleItem();
        timerHandleItem.setItemStr(appointment.getId());
        timerHandleItem.setType(TimerHandleItem.TYPE_SEND_QY_MSG);
        commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
        if(isSendMsg != null && isSendMsg == 1){
            //发送取消预约通知:短信+公众号消息
            timerHandleItem = new TimerHandleItem();
            timerHandleItem.setItemNum(2);
            timerHandleItem.setItemStr(appointment.getId());
            timerHandleItem.setType(TimerHandleItem.TYPE_SEND_SHORT_MSG);
            commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
        }
        //同步到导医
        GuideSyncAppItemUtil.cancle(commonService, appointment.getId());
    }
    /** 查询当天门店转疗订单总数数据 */
    @Override
    public int selectCountIsSwitch(String dayStr, String shopId) {
        return appointmentV2Mapper.selectCountIsSwitch(dayStr, shopId);
    }
    @Override
    public AppointmentInfo selectOneByApptId(String apptId) {
        return appointmentV2Mapper.selectOneByApptId(apptId);
    }
    /////////////////////////////////////////////////////////////////////////////////////////////////
    //检测判断项目是否护士生美
    private void checkProjectIsShengMei(Appointment appointment, boolean isEdit){
        //判断是否项目是否包含护士生美项目
        int count= appointmentV2Mapper.isShengMei(appointment.getProjectIds().contains(",") ? Arrays.asList(appointment.getProjectIds().split(",")) : Arrays.asList(appointment.getProjectIds()));
        log.info("============统计========="+count);
        if(count > 0){
            appointment.setIsLifeBeauty(2);
            //his助手预约时判断
            if(appointment.getAddMode() == Appointment.ADD_MODE_ADVISER_V2_3){
                Shop shop = commonDao.selectOneByKey(ShopMapper.class, appointment.getShopId());
                int shengMeiNum = appointmentV2Mapper.selectCountShengMei(appointment.getShopId(), appointment.getDayStr(), isEdit ? appointment.getId() : null);
                if(shengMeiNum>0 && shop.getShengMeiNum() !=null && shengMeiNum >=shop.getShengMeiNum()&&shop.getShengMeiNum()>0 && appointment.getIsMicApprove() ==0){
                    throw new TipsException("生美预约订单数量已经超过最大值");
                }
            }
        }
    }
    //处理预约涉及的其他数据
    private void handleAppointmentOtherData(boolean isEdit, Appointment appointment
            , List<DoctorTime> doctorTimeList
            , List<AppointmentProject> appointmentProjectList
            , List<AppointmentProjectDeviceNo> appointmentProjectDeviceNoList
            , List<DeviceNoSchedule> deviceNoScheduleList
    ){
        SqlSentence sqlSentence = new SqlSentence();
        Map<String, Object> values = new HashMap<>();
        sqlSentence.setM(values);
        //预约项目---------------------------------------------------------------------------------------
        //把旧的移除掉
        if(isEdit){
            sqlSentence.setSqlSentence(" appId = #{m.apptId} ");
            values.put("apptId", appointment.getId());
            commonDao.deleteWhere(AppointmentProjectMapper.class, sqlSentence);
        }
        //预约项目
        for(AppointmentProject appointmentProject : appointmentProjectList) {
            appointmentProject.setAppId(appointment.getId());
            if(commonDao.insert(AppointmentProjectMapper.class, appointmentProject) != 1){
                throw new TipsException("新增预约项目失败!");
            }
        }
        //门店排班时间-------------------------------------------------------------------------------------
        int count = 0;
        String monthStr = appointment.getDayStr().substring(0, 6);
        ShopTime shopTime = null;
        if(isEdit){
            sqlSentence.setSqlSentence(" select * from shop_time where apptId = #{m.apptId}");
            values.put("apptId", appointment.getId());
            shopTime = commonDao.selectOne(ShopTimeMapper.class, sqlSentence);
        }
        if(shopTime == null) {
            shopTime = new ShopTime(ShopTime.TIME_TYPE_APPT, appointment.getId());
        }
        shopTime.setMonthStr(monthStr);
        shopTime.setDayStr(appointment.getDayStr());
        shopTime.setStartTime(appointment.getStartTime());
        shopTime.setShopId(appointment.getShopId());
        shopTime.setEndTime(appointment.getEndTime());
        if(StringUtils.isEmpty(shopTime.getId())){
            count = commonDao.insert(ShopTimeMapper.class, shopTime);
        }else{
            count = commonDao.updateAll(ShopTimeMapper.class, shopTime);
        }
        if(count != 1){
            throw new TipsException("处理预约门店排班时间失败!");
        }
        //医生面诊和治疗排班时间----------------------------------------------------------------------
        if(isEdit){
            //删除治疗和面诊时间
            values.put("aptId", appointment.getId());
            sqlSentence.setSqlSentence(" aptId = #{m.aptId} and timeType = 2 ");
            commonDao.deleteWhere(DoctorTimeMapper.class, sqlSentence);
        }
        if(StringUtils.noNull(appointment.getDoctorId())){
            //及时面诊开始和结束时间
            Date docComSt, docComEt;
            Calendar ca = Calendar.getInstance();
            ca.setTime(appointment.getStartTime());
            ca.add(Calendar.MINUTE, appointment.getComDuration());
            docComSt = ca.getTime();
            ca.add(Calendar.MINUTE, appointment.getDocComDuration());
            docComEt = ca.getTime();
            //面诊医生时间
            DoctorTime doctorTime = new DoctorTime();
            doctorTime.setTimeType(DoctorTime.TIME_TYPE_APPT);
            doctorTime.setStartTime(docComSt);
            doctorTime.setShopId(appointment.getShopId());
            doctorTime.setMonthTime(appointment.getMonthStr());
            doctorTime.setEndTime(docComEt);
            doctorTime.setDoctorId(appointment.getDoctorId());
            doctorTime.setDayTime(appointment.getDayStr());
            doctorTime.setAptId(appointment.getId());
            doctorTime.setApptType(DoctorTime.APPT_TYPE_COM);
            if(commonDao.insert(DoctorTimeMapper.class, doctorTime) != 1){
                throw new TipsException("新增预约医生面诊时间失败!");
            }
            //医生治疗时间
            if(doctorTimeList != null){
                for(DoctorTime dt : doctorTimeList){
                    dt.setAptId(appointment.getId());
                    if(commonDao.insert(DoctorTimeMapper.class, dt) != 1){
                        throw new TipsException("新增预约医生治疗时间失败!");
                    }
                }
            }
        }
        //设备部分逻辑处理----------------------------------------------------------------------------
        if(isEdit){
            //移除预约项目设备
            sqlSentence.setSqlSentence(" appId = #{m.apptId} ");
            values.put("apptId", appointment.getId());
            commonDao.deleteWhere(AppointmentProjectDeviceNoMapper.class, sqlSentence);
            //移除设备排班
            sqlSentence.setSqlSentence(" aptId = #{m.aptId}");
            values.put("aptId", appointment.getId());
            commonDao.deleteWhere(DeviceNoScheduleMapper.class, sqlSentence);
        }
        //预约关联设备记录
        if(appointmentProjectDeviceNoList != null){
            for(AppointmentProjectDeviceNo apdn : appointmentProjectDeviceNoList) {
                apdn.setAppId(appointment.getId());
                if(commonDao.insert(AppointmentProjectDeviceNoMapper.class, apdn) != 1){
                    throw new TipsException("新增预约项目使用设备信息失败!");
                }
            }
        }
        //设备预约时间占用记录
        if(deviceNoScheduleList != null){
            for(DeviceNoSchedule dns : deviceNoScheduleList) {
                dns.setAptId(appointment.getId());
                if(commonDao.insert(DeviceNoScheduleMapper.class, dns) != 1){
                    throw new TipsException("新增设备排班信息失败!");
                }
            }
        }
    }
    //新增预约 - 处理预约后定时项逻辑
    private void handleTimerHandleItem(Appointment appointment){
        TimerHandleItem timerHandleItem;
        //待审核
        if(appointment.getStatus() == Appointment.STATUS_WAIT){
            //统计新增预约审核成功统计
            timerHandleItem = new TimerHandleItem();
            timerHandleItem.setItemNum(1);
            timerHandleItem.setItemNum1(1);
            timerHandleItem.setItemStr(appointment.getId());
            timerHandleItem.setItemStr1(appointment.getShopId());
            timerHandleItem.setType(TimerHandleItem.TYPE_APP_APPLY);
            timerHandleItem.setCreateTime(appointment.getStartTime());
            commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
            //处理是否发送预约审核通知
            AppointmentConfigUtil.handleAppointmentAuditNotice(commonDao, appointment);
        }else if(appointment.getStatus() == Appointment.STATUS_SUC){
            //预约成功
            timerHandleItem = new TimerHandleItem();
            timerHandleItem.setItemNum(1);
            timerHandleItem.setItemStr(appointment.getId());
            timerHandleItem.setItemStr1(appointment.getShopId());
            timerHandleItem.setType(TimerHandleItem.TYPE_APP_APPLY_SUC);
            timerHandleItem.setCreateTime(appointment.getStartTime());
            commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
            //统计转疗
            if(appointment.getIsSwitch() == BaseEntity.YES){
                timerHandleItem = new TimerHandleItem();
                timerHandleItem.setItemNum(-1);
                timerHandleItem.setItemStr(appointment.getId());
                timerHandleItem.setItemStr1(appointment.getShopId());
                timerHandleItem.setType(TimerHandleItem.TYPE_APP_SWITCH);
                timerHandleItem.setCreateTime(appointment.getStartTime());
                commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
            }
            if(appointment.getIsSendMsg() != null && appointment.getIsSendMsg() == 1){
                //发送短信 暂时不发送 0303
                timerHandleItem = new TimerHandleItem();
                timerHandleItem.setItemNum(1);
                timerHandleItem.setItemStr(appointment.getId());
                timerHandleItem.setType(TimerHandleItem.TYPE_SEND_SHORT_MSG);
                commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
            }
            //发送企业微信消息给顾问
            timerHandleItem = new TimerHandleItem();
            timerHandleItem.setItemStr(appointment.getId());
            timerHandleItem.setType(TimerHandleItem.TYPE_AUDIT_APP_SUC);
            commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
            //新增同步导医处理
            GuideSyncAppItemUtil.syncApp(commonService, appointment.getId());
        }
    }
    /** 编辑预约 - 更新预约成功或者待审数量统计*/
    private void editUpdateAppNumCul(Appointment oldInfo, Appointment appointment)
    {
        //先把原来成功或者待审的移除
        TimerHandleItem timerHandleItem;
        if(oldInfo.getStatus() == Appointment.STATUS_WAIT || oldInfo.getStatus() == Appointment.STATUS_SUC) {
            timerHandleItem = new TimerHandleItem();
            timerHandleItem.setItemNum(-1);
            timerHandleItem.setItemStr(oldInfo.getId());
            timerHandleItem.setItemStr1(oldInfo.getShopId());
            timerHandleItem.setCreateTime(oldInfo.getStartTime());
            if(oldInfo.getStatus() == Appointment.STATUS_WAIT){
                timerHandleItem.setType(TimerHandleItem.TYPE_APP_APPLY);
            }else if(oldInfo.getStatus() == Appointment.STATUS_SUC) {
                timerHandleItem.setType(TimerHandleItem.TYPE_APP_SUC);
            }
            commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
        }
        //新增成功或者待审核的增加
        if(appointment.getStatus() == Appointment.STATUS_WAIT || appointment.getStatus() == Appointment.STATUS_SUC) {
            timerHandleItem = new TimerHandleItem();
            timerHandleItem.setItemNum(1);
            timerHandleItem.setItemStr(appointment.getId());
            timerHandleItem.setItemStr1(appointment.getShopId());
            timerHandleItem.setCreateTime(appointment.getStartTime());
            if (appointment.getStatus() == Appointment.STATUS_WAIT) {
                timerHandleItem.setType(TimerHandleItem.TYPE_APP_APPLY);
                //处理是否发送预约审核通知
                AppointmentConfigUtil.handleAppointmentAuditNotice(commonDao, appointment);
            } else if (appointment.getStatus() == Appointment.STATUS_SUC) {
                timerHandleItem.setType(TimerHandleItem.TYPE_APP_SUC);
                //新增同步导医处理
                GuideSyncAppItemUtil.syncApp(commonService, appointment.getId());
            }
            commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
        }
    }
    /**编辑预约 - 更新下门店的转疗数量*/
    private  void editUpdateShopSwitchNum(Appointment oldInfo, Appointment appointment, Integer isFailEdit)
    {
        TimerHandleItem timerHandleItem = null;
        if(isFailEdit == 1)
        {
            //从审核失败到修改重新提审
            if(appointment.getIsSwitch() == 1)
            {
                timerHandleItem = new TimerHandleItem();
                timerHandleItem.setItemNum(1);
                timerHandleItem.setItemStr(oldInfo.getId());
                timerHandleItem.setItemStr1(appointment.getShopId());
                timerHandleItem.setType(TimerHandleItem.TYPE_APP_SWITCH);
                timerHandleItem.setCreateTime(appointment.getStartTime());
                commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
            }
        }else {
            if (oldInfo.getShopId().equals(appointment.getShopId())) {
                if (oldInfo.getIsSwitch() == BaseEntity.NO && appointment.getIsSwitch() == 1) {
                    timerHandleItem = new TimerHandleItem();
                    timerHandleItem.setItemNum(1);
                    timerHandleItem.setItemStr(oldInfo.getId());
                    timerHandleItem.setItemStr1(oldInfo.getShopId());
                    timerHandleItem.setType(TimerHandleItem.TYPE_APP_SWITCH);
                    timerHandleItem.setCreateTime(appointment.getStartTime());
                    commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
                } else if (oldInfo.getIsSwitch() == BaseEntity.YES && appointment.getIsSwitch() == 0) {
                    timerHandleItem = new TimerHandleItem();
                    timerHandleItem.setItemNum(-1);
                    timerHandleItem.setItemStr(oldInfo.getId());
                    timerHandleItem.setItemStr1(oldInfo.getShopId());
                    timerHandleItem.setType(TimerHandleItem.TYPE_APP_SWITCH);
                    timerHandleItem.setCreateTime(oldInfo.getStartTime());
                    commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
                }
            } else {
                if (oldInfo.getIsSwitch() == BaseEntity.YES) {
                    //原来是转疗的,则把原来的取消
                    timerHandleItem = new TimerHandleItem();
                    timerHandleItem.setItemNum(-1);
                    timerHandleItem.setItemStr(oldInfo.getId());
                    timerHandleItem.setItemStr1(oldInfo.getShopId());
                    timerHandleItem.setType(TimerHandleItem.TYPE_APP_SWITCH);
                    timerHandleItem.setCreateTime(oldInfo.getStartTime());
                    commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
                }
                if (appointment.getIsSwitch() == BaseEntity.YES) {
                    //当前是转疗,则添加现在的
                    timerHandleItem = new TimerHandleItem();
                    timerHandleItem.setItemNum(1);
                    timerHandleItem.setItemStr(oldInfo.getId());
                    timerHandleItem.setItemStr1(appointment.getShopId());
                    timerHandleItem.setType(TimerHandleItem.TYPE_APP_SWITCH);
                    timerHandleItem.setCreateTime(appointment.getStartTime());
                    commonDao.insert(TimerHandleItemMapper.class, timerHandleItem);
                }
            }
        }
    }
}
phi_platform_user/src/main/java/com/hx/phip/service/impl/EmployeeServiceImpl.java
@@ -105,4 +105,10 @@
    public EmployeeRole selectRoleInfoById(String id) {
        return employeeMapper.selectRoleInfoById(id);
    }
    /** 查询员工 By id and role */
    @Override
    public Employee selectOneByIdAndRole(String id, String roleUniqueStr) {
        return employeeMapper.selectOneByIdAndRole(id, roleUniqueStr);
    }
}