| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530 |
- package com.ylx.massage.service.impl;
- import cn.hutool.core.collection.CollectionUtil;
- import cn.hutool.json.JSONException;
- import cn.hutool.json.JSONUtil;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.ylx.common.config.WechatAccountConfig;
- import com.ylx.common.constant.MassageConstants;
- import com.ylx.common.exception.ServiceException;
- import com.ylx.common.utils.StringUtils;
- import com.ylx.massage.domain.*;
- import com.ylx.massage.domain.vo.TJsVo;
- import com.ylx.massage.enums.JsStatusEnum;
- import com.ylx.massage.mapper.TJsMapper;
- import com.ylx.massage.service.*;
- import com.ylx.massage.utils.DateTimeUtils;
- import com.ylx.massage.utils.LocationUtil;
- import com.ylx.massage.utils.VirtualTechnicianNicknameGenerator;
- import com.ylx.massage.utils.WeChatUtil;
- import com.ylx.order.service.TOrderService;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import javax.annotation.Resource;
- import java.time.LocalDateTime;
- import java.util.*;
- import java.util.stream.Collectors;
- /**
- * 技师表 服务实现类
- */
- @Service
- @Slf4j
- public class TJsServiceImpl extends ServiceImpl<TJsMapper, TJs> implements TJsService {
- @Resource
- private TJsMapper mapper;
- @Resource
- private WechatAccountConfig wxPayProperties;
- @Resource
- private LocationUtil locationUtil;
- @Resource
- private TCollectService collectService;
- @Resource
- private TXiangmuService xiangmuService;
- @Resource
- private TCommentService commentService;
- @Resource
- private TWxUserService wxUserService;
- @Resource
- private WeChatUtil weChatUtil;
- @Resource
- private TOrderService orderService;
- @Resource
- private TSignService signService;
- @Resource(name = "commonAsyncExecutor")
- private ThreadPoolTaskExecutor threadPoolTaskExecutor;
- @Resource
- private VirtualTechnicianNicknameGenerator nicknameGenerator;
- @Override
- public Page<TJs> getAll(Page<TJs> page, TJsVo param) {
- return mapper.getAll(page, param);
- }
- @Override
- public boolean addJs(TJs js) {
- LambdaQueryWrapper<TJs> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(TJs::getcOpenId, js.getcOpenId());
- TJs cjs = this.getOne(queryWrapper);
- if (cjs != null) {
- throw new ServiceException("请勿重复申请");
- }
- // cacheAddress(js);
- initialization(js);
- return this.save(js);
- }
- private void cacheAddress(TJs js) {
- if (StringUtils.isBlank(js.getcPhone())) {
- throw new ServiceException("手机号不能为空");
- }
- if (StringUtils.isBlank(js.getName())) {
- throw new ServiceException("地址不能为空");
- }
- if (StringUtils.isNotBlank(js.getId())) {
- TJs oldJs = this.getById(js.getId());
- if (oldJs.getnTong().equals(JsStatusEnum.JS_RETURN.getCode())) {
- js.setnTong(JsStatusEnum.JS_NOT_PASS.getCode());
- }
- }
- if (StringUtils.isNotBlank(js.getName())) {
- this.updateLocation(js);
- }
- // log.info("js:{}", js);
- // JSONObject jsonObject = new JSONObject(js.getcAddress());
- // Object latitude = jsonObject.get("latitude");
- // Object longitude = jsonObject.get("longitude");
- // locationUtil.geoAdd(LocationUtil.GEO_KEY,js.getcOpenId(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
- }
- @Override
- public boolean wxUpdateJs(TJs js) {
- cacheAddress(js);
- return this.updateById(js);
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public boolean auditing(TJs js) {
- TJs js1 = this.getById(js.getId());
- LambdaQueryWrapper<TJs> jsLambdaQueryWrapper = new LambdaQueryWrapper<>();
- jsLambdaQueryWrapper.eq(TJs::getId, js.getId()).eq(TJs::getnTong, JsStatusEnum.JS_NOT_PASS.getCode());
- js.setnTong(JsStatusEnum.JS_PASS.getCode());
- js.setnB2(1);
- js.setcPhone(js1.getcPhone());
- js.setcTime(new Date());
- LambdaQueryWrapper<TWxUser> wxLambdaQueryWrapper = new LambdaQueryWrapper<>();
- wxLambdaQueryWrapper.eq(TWxUser::getcOpenid, js1.getcOpenId());
- TWxUser tWxUser = new TWxUser();
- //角色状态修改
- tWxUser.setRole(1);
- wxUserService.update(tWxUser, wxLambdaQueryWrapper);
- //修改审核状态
- boolean update = this.update(js, jsLambdaQueryWrapper);
- //修改新技师标识
- /* js.setcOpenId(js1.getcOpenId());
- // 发送审核结果通知
- this.jsNotification(js);*/
- return update;
- }
- @Override
- public void jsNotification(TJs js) {
- try {
- cn.hutool.json.JSONObject param = JSONUtil.createObj();
- //是否审核通过
- if (JsStatusEnum.JS_PASS.getCode().equals(js.getnTong())) {
- param.set("const1", JSONUtil.createObj().set("value", "审核通过可以接单"));
- } else {
- param.set("const1", JSONUtil.createObj().set("value", "审核驳回请重新提交"));
- }
- //审核时间
- param.set("time2", JSONUtil.createObj().set("value", DateTimeUtils.formatDate(new Date(), DateTimeUtils.DATE_NUMBER_YEAR_MONTH_FORMAT)));
- //账号
- param.set("character_string3", JSONUtil.createObj().set("value", js.getcPhone()));
- //
- weChatUtil.notification(js.getcOpenId(), wxPayProperties.getTemplateId2(), param);
- } catch (JSONException e) {
- e.printStackTrace();
- throw new RuntimeException(e);
- }
- }
- @Override
- public boolean updateLocation(TJs js) {
- if (StringUtils.isBlank(js.getId())) {
- throw new ServiceException("ID不能为空");
- }
- if (StringUtils.isBlank(js.getName())) {
- throw new ServiceException("地址不能为空");
- }
- // JSONObject jsonObject = new JSONObject(js.getcAddress());
- // Object latitude = jsonObject.get("latitude");
- // Object longitude = jsonObject.get("longitude");
- locationUtil.geoAdd(LocationUtil.GEO_KEY, js.getcPhone(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
- return this.updateById(js);
- }
- @Override
- public void newOrderNotification(String openId) {
- // 订单号 23010000010101
- // 用户电话 15055556666
- // 用户名称 李四
- // 时间 2022年11月11日22:22
- // 服务地址 湖南长沙岳麓
- /*cn.hutool.json.JSONObject param = JSONUtil.createObj();
- //订单号
- param.set("character_string9", JSONUtil.createObj().set("value", "23010000010101"));
- //点话
- param.set("phone_number14", JSONUtil.createObj().set("value", "18360233903"));
- param.set("thing18", JSONUtil.createObj().set("value", "李四"));
- param.set("time6", JSONUtil.createObj().set("value", "2022年11月11日 22:22"));
- param.set("thing27", JSONUtil.createObj().set("value", "湖南长沙岳麓"));*/
- cn.hutool.json.JSONObject param = JSONUtil.createObj();
- /**
- * 接单成功通知
- */
- //设置订单号
- param.set("character_string1", JSONUtil.createObj().set("value", "123456"));
- //设置项目
- param.set("thing4", JSONUtil.createObj().set("value", "按摩"));
- //商家名称
- param.set("thing9", JSONUtil.createObj().set("value", "李强"));
- //联系电话
- param.set("phone_number5", JSONUtil.createObj().set("value", "15296619636"));
- weChatUtil.notification(openId, wxPayProperties.getUserTemplate1(), param);
- }
- @Override
- public boolean upPost(TJs js) {
- if (StringUtils.isBlank(js.getId())) {
- throw new ServiceException("技师ID不能为空");
- }
- if (js.getnStatus2() == null) {
- throw new ServiceException("岗位状态不能为空");
- }
- TJs js2 = this.getById(js.getId());
- TSign tSign = new TSign();
- //上岗
- if (Objects.equals(js.getnStatus2(), JsStatusEnum.POST_ON_DUTY.getCode())) {
- //岗位状态为1时,设置nStatus为1
- js.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
- //记录签到信息
- tSign.setSingTime(new Date());
- tSign.setOpenId(js2.getcOpenId());
- tSign.setJsId(js2.getId());
- tSign.setName(js2.getcName());
- tSign.setDeptId(js2.getDeptId());
- tSign.setDeptName(js2.getCity());
- signService.save(tSign);
- }
- if (Objects.equals(js.getnStatus2(), JsStatusEnum.POST_NOT_ON_DUTY.getCode())) {
- //判断该技师是否有服务中的订单
- TJs js1 = this.getById(js.getId());
- if (js1.getnStatus().equals(JsStatusEnum.JS_SERVICE.getCode())) {
- throw new ServiceException("您有服务中的订单,不能下岗");
- }
- js.setnStatus(JsStatusEnum.JS_NO_SERVICE.getCode());
- LambdaQueryWrapper<TSign> signLambdaQueryWrapper = new LambdaQueryWrapper<>();
- signLambdaQueryWrapper.eq(TSign::getJsId, js.getId()).isNull(TSign::getLayoffTime).orderByAsc(TSign::getSingTime).last("LIMIT 1");
- TSign singn = signService.getOne(signLambdaQueryWrapper);
- if (null != singn) {
- singn.setLayoffTime(new Date());
- Long time = getaTime(singn);
- singn.setOnlineTime(time.intValue());
- signService.updateById(singn);
- }
- // else {
- // tSign.setSingTime(DateTimeUtils.getStartDate(new Date()));
- // tSign.setLayoffTime(new Date());
- // tSign.setOpenId(js2.getcOpenId());
- // tSign.setJsId(js2.getId());
- // tSign.setName(js2.getcName());
- // Long time = getaTime(tSign);
- // tSign.setDeptId(js2.getDeptId());
- // tSign.setDeptName(js2.getCity());
- // tSign.setOnlineTime(time.intValue());
- // signService.save(tSign);
- // }
- }
- return this.updateById(js);
- }
- private Long getaTime(TSign tSign) {
- Long layoff = DateTimeUtils.dateToStamp(tSign.getLayoffTime());
- Long sing = DateTimeUtils.dateToStamp(tSign.getSingTime());
- Long time = layoff - sing;
- time = time / 1000;
- time = time / 60;
- return time;
- }
- @Override
- public boolean block(TJs js) {
- if (StringUtils.isBlank(js.getId())) {
- throw new ServiceException("技师ID不能为空");
- }
- if (js.getnTong() == null) {
- throw new ServiceException("拉黑状态不能为空");
- }
- //恢复
- if (Objects.equals(js.getnTong(), JsStatusEnum.JS_NOT_PASS.getCode())) {
- //岗位状态为1时,设置nStatus为1
- js.setnStatus(JsStatusEnum.JS_NO_SERVICE.getCode());
- }
- if (Objects.equals(js.getnTong(), JsStatusEnum.BLOCKED.getCode())) {
- //判断该技师是否有服务中的订单
- TJs js1 = this.getById(js.getId());
- if (js1.getnStatus().equals(JsStatusEnum.JS_SERVICE.getCode())) {
- throw new ServiceException("有服务中的订单,暂时不能拉黑");
- }
- js.setnStatus(JsStatusEnum.JS_NO_SERVICE.getCode());
- js.setnStatus2(JsStatusEnum.POST_NOT_ON_DUTY.getCode());
- }
- return this.updateById(js);
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public boolean pcAddJs(TJs js) {
- /*if (js.getCity() == null) {
- throw new ServiceException("所在城市不能为空");
- }*/
- //判断生活照图片列表不能为空
- if (js.getcImgList() == null || js.getcImgList().isEmpty()) {
- throw new ServiceException("生活照图片列表不能为空");
- }
- //判断身份证图片列表不能为空
- if (js.getcSfzImg() == null || js.getcSfzImg().isEmpty()) {
- throw new ServiceException("身份证图片列表不能为空");
- }
- //判断虚拟订单数量不能为空
- if (js.getVirtualOrderNumber() == null) {
- throw new ServiceException("虚拟订单数量不能为空");
- }
- if (js.getVirtualTechNumber() == null) {
- // 默认值为10
- js.setVirtualTechNumber(10);
- }
- // 验证虚拟技师数量
- Integer virtualTechNumber = js.getVirtualTechNumber();
- if (virtualTechNumber < 0) {
- throw new ServiceException("虚拟技师数量不能为负数");
- }
- // 0-真实技师
- js.setTechType(0);
- //设置性别,默认:女
- js.setnSex(0);
- //设置服务状态,默认:可服务
- js.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
- // 上岗状态:默认为未上岗
- js.setnStatus2(JsStatusEnum.POST_NOT_ON_DUTY.getCode());
- // 审核状态:默认为待审核
- js.setnTong(JsStatusEnum.JS_NOT_PASS.getCode());
- js.setDtCreateTime(LocalDateTime.now());
- this.save(js);
- // 如果需要生成虚拟技师
- if (virtualTechNumber > 0) {
- log.info("开始为真实技师[{}]生成{}个虚拟技师", js.getcName(), virtualTechNumber);
- generateVirtualTechnicians(js, virtualTechNumber);
- }
- String token = weChatUtil.getToken();
- log.info("获取到的token: {}", token);
- //获取二维码ticket
- Map<?, ?> jsTicket = weChatUtil.getJsTicket(token, js.getId());
- log.info("获取到的jsTicket: {}", jsTicket);
- js.setTicket(jsTicket.get("ticket").toString());
- return this.updateById(js);
- }
- /**
- * 批量生成虚拟技师
- * <p>
- * 业务规则:
- * <ul>
- * <li>虚拟技师类型为 techType = 1</li>
- * <li>除昵称外,其他信息与真实技师完全一致</li>
- * <li>昵称随机生成(2-3个汉字,偏女性化,全表唯一)</li>
- * <li>虚拟技师不需要手机号、OpenID等敏感信息</li>
- * </ul>
- * </p>
- *
- * @param realTechnician 真实技师信息
- * @param virtualTechNumber 需要生成的虚拟技师数量
- */
- private void generateVirtualTechnicians(TJs realTechnician, Integer virtualTechNumber) {
- try {
- // 批量生成唯一昵称
- List<String> nicknames = nicknameGenerator.batchGenerateUniqueNicknames(virtualTechNumber);
- log.info("成功生成{}个唯一昵称", nicknames.size());
- // 批量创建虚拟技师对象
- List<TJs> virtualTechnicians = new ArrayList<>(virtualTechNumber);
- LocalDateTime now = LocalDateTime.now();
- for (String nickname : nicknames) {
- TJs virtualJs = new TJs();
- // 复制真实技师的所有字段(除ID、昵称、技师类型外)
- virtualJs.setcNickName(nickname); // 随机生成的昵称
- virtualJs.setTechType(1); // 1-虚拟技师
- virtualJs.setcName(realTechnician.getcName()); // 真实姓名(保持一致)
- virtualJs.setcPortrait(realTechnician.getcPortrait()); // 头像
- virtualJs.setcPhone(realTechnician.getcPhone());
- virtualJs.setcOpenId(realTechnician.getcOpenId());
- virtualJs.setcAddress(realTechnician.getcAddress()); // 地址
- virtualJs.setnSex(realTechnician.getnSex()); // 性别
- virtualJs.setcImgList(realTechnician.getcImgList()); // 生活照列表
- virtualJs.setcSfzImg(realTechnician.getcSfzImg()); // 身份证图片
- virtualJs.setcVideo(realTechnician.getcVideo()); // 视频介绍
- virtualJs.setcBhList(realTechnician.getcBhList()); // 可预约项目列表
- virtualJs.setHealthCertificate(realTechnician.getHealthCertificate()); // 健康证
- virtualJs.setBusinessLicense(realTechnician.getBusinessLicense()); // 营业执照
- virtualJs.setCertification(realTechnician.getCertification()); // 资格证
- virtualJs.setNoCrime(realTechnician.getNoCrime()); // 无犯罪记录
- virtualJs.setDeptId(realTechnician.getDeptId()); // 部门ID
- virtualJs.setDaytimeMileage(realTechnician.getDaytimeMileage()); // 白天免车费公里数
- virtualJs.setNigthMileage(realTechnician.getNigthMileage()); // 夜间免车费公里数
- virtualJs.setcJianjie(realTechnician.getcJianjie()); // 个人简介
- virtualJs.setnStar(MassageConstants.INTEGER_FIVE); // 评分
- virtualJs.setnBili(realTechnician.getnBili()); // 佣金比例
- virtualJs.setLongitude(realTechnician.getLongitude()); // 经度
- virtualJs.setLatitude(realTechnician.getLatitude()); // 纬度
- // 服务状态,默认:可服务
- virtualJs.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
- virtualJs.setnStatus2(JsStatusEnum.POST_NOT_ON_DUTY.getCode()); // 上岗状态
- virtualJs.setnTong(JsStatusEnum.JS_NOT_PASS.getCode()); // 审核状态
- virtualJs.setnB1(realTechnician.getnB1());
- virtualJs.setnB2(realTechnician.getnB2());
- virtualJs.setnB3(realTechnician.getnB3());
- virtualJs.setDtCreateTime(now); // 创建时间
- virtualJs.setVirtualOrderNumber(realTechnician.getVirtualOrderNumber()); // 虚拟订单数量
- virtualJs.setVirtualTechNumber(0); // 虚拟技师数量(虚拟技师不再生成虚拟技师)
- virtualJs.setRecording(realTechnician.getRecording()); // 录音
- virtualJs.setVideoRecording(realTechnician.getVideoRecording()); // 录像
- virtualJs.setCommitment(realTechnician.getCommitment()); // 承诺书
- virtualJs.setCity(realTechnician.getCity()); // 所在城市
- virtualJs.setJsGrade(realTechnician.getJsGrade()); // 技师等级
- virtualTechnicians.add(virtualJs);
- }
- // 批量保存虚拟技师
- boolean saveResult = this.saveBatch(virtualTechnicians);
- if (saveResult) {
- log.info("成功批量保存{}个虚拟技师", virtualTechnicians.size());
- } else {
- throw new ServiceException("批量保存虚拟技师失败");
- }
- } catch (Exception e) {
- log.error("生成虚拟技师失败", e);
- throw new ServiceException("生成虚拟技师失败:" + e.getMessage());
- } finally {
- // 清理昵称缓存,释放内存
- nicknameGenerator.clearCache();
- }
- }
- @Override
- public TJs getByJsId(String jsId, String openId) {
- if (jsId == null || jsId.trim().isEmpty()) {
- // 处理空或空白的jsId
- throw new ServiceException("Id为空");
- }
- TJs js = this.getById(jsId);
- if (js == null) {
- // 处理getById返回null的情况
- return null; // 或者返回一个新的TJs实例,具体看业务需求
- }
- if (StringUtils.isBlank(js.getcBhList())) {
- // 处理js.getcBhList()返回null或空列表的情况
- js.setProjects(new ArrayList<>()); // 设置空列表,避免后续调用空指针
- return js;
- }
- //查询项目
- List<String> projectIds = Arrays.stream(js.getcBhList().split(",")).collect(Collectors.toList());
- LambdaQueryWrapper<TXiangmu> xiangmuLambdaQueryWrapper = new LambdaQueryWrapper<>();
- xiangmuLambdaQueryWrapper.in(TXiangmu::getcId, projectIds).orderByAsc(TXiangmu::getdPrice);
- List<TXiangmu> projects = xiangmuService.list(xiangmuLambdaQueryWrapper);
- js.setProjects(projects);
- //查询评论
- LambdaQueryWrapper<TComment> commentLambdaQueryWrapper = new LambdaQueryWrapper<>();
- commentLambdaQueryWrapper.eq(TComment::getcJsid, jsId);
- List<TComment> list = commentService.list(commentLambdaQueryWrapper);
- if (CollectionUtil.isNotEmpty(list)) {
- js.setComments(list);
- }
- //是否收藏
- LambdaQueryWrapper<TCollect> objectLambdaQueryWrapper = new LambdaQueryWrapper<>();
- objectLambdaQueryWrapper.eq(TCollect::getcJsId, jsId).eq(TCollect::getcWxOpenId, openId);
- TCollect collect = collectService.getOne(objectLambdaQueryWrapper);
- if (collect != null) {
- js.setIsCollection(true);
- } else {
- js.setIsCollection(false);
- }
- return js;
- }
- private static void initialization(TJs js) {
- // 评分默认最高 5
- js.setnStar(MassageConstants.INTEGER_FIVE);
- // 已服务数量 0
- js.setnNum(MassageConstants.INTEGER_ZERO);
- // 佣金比例 10
- js.setnBili(MassageConstants.INTEGER_TEN);
- // 服务状态
- js.setnStatus(JsStatusEnum.JS_NO_SERVICE.getCode());
- // 上岗状态
- js.setnStatus2(JsStatusEnum.POST_NOT_ON_DUTY.getCode());
- // 审核状态
- js.setnTong(JsStatusEnum.JS_NOT_PASS.getCode());
- js.setnB1(MassageConstants.INTEGER_ZERO);
- js.setnB2(MassageConstants.INTEGER_ZERO);
- js.setnB3(MassageConstants.INTEGER_ZERO);
- js.setTechType(MassageConstants.INTEGER_ZERO);
- js.setDtCreateTime(LocalDateTime.now());
- }
- }
|