jinshihui 3 днів тому
батько
коміт
f5205fa30f
23 змінених файлів з 1154 додано та 137 видалено
  1. 35 5
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TJsController.java
  2. 73 2
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TOrderController.java
  3. 17 2
      nightFragrance-admin/src/main/resources/application-dev.yml
  4. 20 6
      nightFragrance-admin/src/main/resources/application-test.yml
  5. 1 1
      nightFragrance-admin/src/main/resources/application.yml
  6. 10 19
      nightFragrance-common/src/main/java/com/ylx/common/exception/ServiceException.java
  7. 4 1
      nightFragrance-framework/src/main/java/com/ylx/framework/security/filter/JwtAuthenticationTokenFilter.java
  8. 1 1
      nightFragrance-framework/src/main/java/com/ylx/framework/web/service/WxTokenService.java
  9. 127 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/OrderAllocationLog.java
  10. 20 4
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TJs.java
  11. 25 8
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TOrder.java
  12. 38 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/OrderAllocationResultVo.java
  13. 4 0
      nightFragrance-massage/src/main/java/com/ylx/massage/enums/JsStatusEnum.java
  14. 17 0
      nightFragrance-massage/src/main/java/com/ylx/massage/mapper/OrderAllocationLogMapper.java
  15. 73 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/OrderAllocationLogService.java
  16. 100 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/OrderAllocationLogServiceImpl.java
  17. 3 3
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/OrderValidationServiceImpl.java
  18. 113 6
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TJsServiceImpl.java
  19. 189 25
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TOrderServiceImpl.java
  20. 4 2
      nightFragrance-massage/src/main/java/com/ylx/massage/task/massageTask.java
  21. 253 0
      nightFragrance-massage/src/main/java/com/ylx/massage/utils/VirtualTechnicianNicknameGenerator.java
  22. 5 0
      nightFragrance-massage/src/main/resources/mapper/massage/TOrderMapper.xml
  23. 22 52
      nightFragrance-quartz/src/main/java/com/ylx/quartz/controller/SysJobController.java

+ 35 - 5
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TJsController.java

@@ -95,7 +95,12 @@ public class TJsController extends BaseController {
     @RequestMapping(value = "pc/add", method = RequestMethod.POST)
     @ApiOperation("PC添加技师申请")
     public R pcAdd(@RequestBody TJs js) {
-        return R.ok(jsService.pcAddJs(js));
+        try {
+            return R.ok(jsService.pcAddJs(js));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
     }
 
     /**
@@ -201,7 +206,7 @@ public class TJsController extends BaseController {
 
 
     /**
-     * 微信查询技师列表
+     * 微信查询技师列表(用户端)
      *
      * @param page
      * @param js
@@ -257,7 +262,7 @@ public class TJsController extends BaseController {
 
 
     /**
-     * 分页查询技师数据
+     * 分页查询技师数据(PC端)
      *
      * @param page
      * @param js
@@ -270,27 +275,52 @@ public class TJsController extends BaseController {
         log.info("loginUser:{}", JSONUtil.toJsonStr(loginUser));
 
         LambdaQueryWrapper<TJs> mhCompanyLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        mhCompanyLambdaQueryWrapper.eq(null != js.getnTong(), TJs::getnTong, js.getnTong()).
+        mhCompanyLambdaQueryWrapper.
                 like(StringUtils.isNotBlank(js.getcName()), TJs::getcName, js.getcName()).
                 like(StringUtils.isNotBlank(js.getcNickName()), TJs::getcNickName, js.getcNickName()).
                 like(StringUtils.isNotBlank(js.getcPhone()), TJs::getcPhone, js.getcPhone()).
                 eq(js.getnStatus() != null, TJs::getnStatus, js.getnStatus()).
                 eq(js.getnSex() != null, TJs::getnSex, js.getnSex()).
                 eq(js.getDeptId() != null, TJs::getDeptId, js.getDeptId()).
-                orderByDesc(TJs::getDtCreateTime);
+                eq(TJs::getTechType, 0) // 只查询真实技师的
+                .orderByDesc(TJs::getDtCreateTime);
         //部门数据
         if (loginUser.getDeptId() != 100) {
             mhCompanyLambdaQueryWrapper.eq(TJs::getDeptId, loginUser.getDeptId().toString());
         }
+        //查询技师数据
         if (js.getPageType() == 1) {
             mhCompanyLambdaQueryWrapper.eq(TJs::getnTong, JsStatusEnum.JS_PASS.getCode());
         } else {
+            // 查询审核页的数据
             mhCompanyLambdaQueryWrapper.in(TJs::getnTong, Arrays.asList(JsStatusEnum.JS_RETURN.getCode(), JsStatusEnum.JS_NOT_PASS.getCode(), JsStatusEnum.BLOCKED.getCode()));
         }
         return jsService.page(page, mhCompanyLambdaQueryWrapper);
     }
 
 
+    /**
+     * 分页查询可服务的真实技师列表
+     *
+     * @param page
+     * @param js
+     * @return R<Page < TJs>>
+     */
+    @ApiOperation("分页查询可服务的真实技师列表")
+    @RequestMapping(value = "/queryRealTechnicians", method = RequestMethod.GET)
+    public R<Page<TJs>> page(Page<TJs> page, TJs js) {
+        LambdaQueryWrapper<TJs> mhCompanyLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        mhCompanyLambdaQueryWrapper.
+                like(StringUtils.isNotBlank(js.getcName()), TJs::getcName, js.getcName()).
+                eq(TJs::getnStatus, 0).
+                eq(TJs::getTechType, 0)
+                .eq(TJs::getnTong, JsStatusEnum.JS_PASS.getCode())
+                .eq(TJs::getnStatus2, 0)
+                .orderByDesc(TJs::getnNum); //已服务数量降序排序
+        return R.ok(jsService.page(page, mhCompanyLambdaQueryWrapper));
+    }
+
+
     /**
      * 删除技师
      *

+ 73 - 2
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TOrderController.java

@@ -11,15 +11,22 @@ import com.ylx.common.enums.BusinessType;
 import com.ylx.common.exception.ServiceException;
 import com.ylx.common.utils.StringUtils;
 import com.ylx.common.utils.poi.ExcelUtil;
+import com.ylx.massage.domain.TJs;
 import com.ylx.massage.domain.TOrder;
+import com.ylx.massage.domain.vo.OrderAllocationResultVo;
 import com.ylx.massage.domain.vo.OrderVerificationVo;
 import com.ylx.massage.enums.Enumproject;
 import com.ylx.massage.enums.OrderStatusEnum;
 import com.ylx.massage.enums.OrderStatusEnumVo;
+import com.ylx.massage.domain.OrderAllocationLog;
+import com.ylx.massage.service.TJsService;
 import com.ylx.massage.service.TOrderService;
+import com.ylx.massage.service.OrderAllocationLogService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
 import org.springframework.web.bind.annotation.*;
 
 
@@ -29,6 +36,7 @@ import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * 订单表 前端控制器
@@ -41,6 +49,12 @@ public class TOrderController extends BaseController {
     @Resource
     private TOrderService orderService;
 
+    @Resource
+    private OrderAllocationLogService allocationLogService;
+
+    @Resource
+    private TJsService jsService;
+
 
     /**
      * 下单
@@ -86,7 +100,12 @@ public class TOrderController extends BaseController {
         }
     }
 
-
+    /**
+     * 转单
+     *
+     * @param order
+     * @return R<TOrder>
+     */
     @Log(title = "转单", businessType = BusinessType.INSERT)
     @ApiOperation("转单")
     @RequestMapping(value = "wx/transferOrder", method = RequestMethod.POST)
@@ -302,7 +321,7 @@ public class TOrderController extends BaseController {
 
 
     /**
-     * PC获取订单信息
+     * PC获取订单信息(PC端)
      *
      * @param page
      * @param order
@@ -352,6 +371,8 @@ public class TOrderController extends BaseController {
         return R.ok(pageSelect);
     }
 
+
+
     @ApiOperation("更新订单数据")
     @Log(title = "更新订单数据", businessType = BusinessType.OTHER)
     @RequestMapping(value = "/update", method = RequestMethod.POST)
@@ -393,4 +414,54 @@ public class TOrderController extends BaseController {
         List<Enumproject> statusEnum = OrderStatusEnumVo.getStatusEnum();
         return R.ok(statusEnum);
     }
+
+    /**
+     * 分页查询订单分配操作记录
+     *
+     * @param page 分页参数
+     * @param orderNo 订单号
+     * @return R 分页结果
+     */
+    @Log(title = "分页查询订单分配操作记录", businessType = BusinessType.OTHER)
+    @ApiOperation("分页查询订单分配操作记录")
+    @RequestMapping(value = "/allocationLogs/page", method = RequestMethod.GET)
+    public R getAllocationLogsPage(Page<OrderAllocationLog> page, @RequestParam String orderNo) {
+        try {
+            // 构建查询条件
+            LambdaQueryWrapper<OrderAllocationLog> wrapper = new LambdaQueryWrapper<>();
+            // 订单号条件
+            wrapper.eq(StringUtils.isNotBlank(orderNo), OrderAllocationLog::getOrderNo, orderNo);
+            // 按创建时间倒序排列
+            wrapper.orderByDesc(OrderAllocationLog::getCreateTime);
+
+            // 分页查询
+            Page<OrderAllocationLog> resultPage = allocationLogService.page(page, wrapper);
+
+            // 转换为VO列表
+            List<OrderAllocationResultVo> voList = resultPage.getRecords().stream()
+                    .map(log -> {
+                        // 转换为VO
+                        OrderAllocationResultVo vo = new OrderAllocationResultVo();
+                        BeanUtils.copyProperties(log, vo);
+                        //根据newTechnicianId查询新技师电话
+                        if (StringUtils.isNotBlank(vo.getNewTechnicianId())) {
+                            TJs technician = jsService.getById(log.getNewTechnicianId());
+                            if (technician != null) {
+                                vo.setNewTechnicianPhone(technician.getcPhone());
+                            }
+                        }
+                        return vo;
+                    }).collect(Collectors.toList());
+
+            // 创建新的分页对象,包装转换后的VO列表
+            Page<OrderAllocationResultVo> voPage = new Page<>(page.getCurrent(), page.getSize(), resultPage.getTotal());
+            voPage.setRecords(voList);
+            voPage.setPages(resultPage.getPages());
+
+            return R.ok(voPage);
+        } catch (Exception e) {
+            log.error("分页查询订单分配记录失败 - 错误信息:{}", e.getMessage(), e);
+            return R.fail("分页查询分配记录失败:" + e.getMessage());
+        }
+    }
 }

+ 17 - 2
nightFragrance-admin/src/main/resources/application-dev.yml

@@ -7,7 +7,7 @@ ylx:
   # 版权年份
   copyrightYear: 2024
   # 文件路径 示例( Windows配置D:/nightFragrance/uploadPath,Linux配置 /home/nightFragrance/uploadPath)
-  profile: D:/nightFragrance/uploadPath
+  profile: E:\nightFragrance\uploadPath
   # 获取ip地址开关
   addressEnabled: false
   # 验证码类型 math 数字计算 char 字符验证
@@ -241,7 +241,6 @@ wx:
 
   # 微信公众号
 wechat:
-
 #  mpAppId: wxa408092ddcec15b8
 #  mpAppSecret: 3d0953e32e84180b945ace15050365c6
   mpAppId: wxa72f643173a90106
@@ -255,6 +254,22 @@ wechat:
   # 消息模版ID
   template-id-1: HU2LfMIes91Au9kxR3VEoNYuMayxZoPNsFRfWNCmKrQ
 
+  # 用户侧
+  # 已接单提醒消息模版ID
+  userTemplate1: P4C-9-BEOUbLgvRbS5ea83lvwDWruH9So2Al2-uFk9M
+  # 订单完成提醒消息模版ID
+  userTemplate2: ZGI3Bto2fZaGjXPaw5ad04GgDhVHkqWxyyIULDSAuao
+  # 取消订单提醒消息模版ID
+  userTemplate3: Ed4c0ra3qFl-WjgaxomYQUZ2g45XxAjxUVhIXFHXQ8U
+
+  # 技师侧
+  # 待接单提醒消息模版ID
+  techTemplate1: xPuD5uBpRj7ui79pIWQgDngiOysW0zS7bq1yPyGNhdw
+  # 订单完成提醒消息模版ID
+  techTemplate2: P4C-9-BEOUbLgvRbS5ea8x98iG2J6_QeGlKn-C0iNNk
+  # 取消订单提醒消息模版ID
+  techTemplate3: Ed4c0ra3qFl-WjgaxomYQWRCgjkO5NfJkibcFOTx3-Q
+
 # 防止XSS攻击
 xss:
   # 过滤开关

+ 20 - 6
nightFragrance-admin/src/main/resources/application-test.yml

@@ -129,15 +129,13 @@ spring:
   # redis 配置
   redis:
     # 地址
-    #host: 172.24.144.218
-    host: 127.0.0.1
+    host: 172.24.144.218
     # 端口,默认为6379
     port: 6379
     # 数据库索引
     database: 1
     # 密码
-    #password: sxzgkj@2023
-    password: 123456
+    password: sxzgkj@2023
     # 连接超时时间
     timeout: 10s
     lettuce:
@@ -237,7 +235,6 @@ wx:
 
   # 微信公众号
 wechat:
-
   mpAppId: wxa72f643173a90106
   mpAppSecret: 6ceb4647506a4f1654bb10773b227357
   # 获取code
@@ -247,9 +244,26 @@ wechat:
   # 回调地址
   access-token-url: https://api.weixin.qq.com/sns/oauth2/access_token
   # 消息模版ID订单待接单通知
-  template-id-1: HU2LfMIes91Au9kxR3VEoNYuMayxZoPNsFRfWNCmKrQ
+  #template-id-1: HU2LfMIes91Au9kxR3VEoNYuMayxZoPNsFRfWNCmKrQ
   # 入驻平台审核通知
   template-id-2: els5FNc0BtFbSKhxIjbzqGX_9FmxAWyBhjL95qVevYE
+
+  # 用户侧
+  # 已接单提醒消息模版ID
+  userTemplate1: P4C-9-BEOUbLgvRbS5ea83lvwDWruH9So2Al2-uFk9M
+  # 订单完成提醒消息模版ID
+  userTemplate2: ZGI3Bto2fZaGjXPaw5ad04GgDhVHkqWxyyIULDSAuao
+  # 取消订单提醒消息模版ID
+  userTemplate3: Ed4c0ra3qFl-WjgaxomYQUZ2g45XxAjxUVhIXFHXQ8U
+
+  # 技师侧
+  # 待接单提醒消息模版ID
+  techTemplate1: xPuD5uBpRj7ui79pIWQgDngiOysW0zS7bq1yPyGNhdw
+  # 订单完成提醒消息模版ID
+  techTemplate2: P4C-9-BEOUbLgvRbS5ea8x98iG2J6_QeGlKn-C0iNNk
+  # 取消订单提醒消息模版ID
+  techTemplate3: Ed4c0ra3qFl-WjgaxomYQWRCgjkO5NfJkibcFOTx3-Q
+
   # 菜单
   menu: "{\"button\":[{\"type\":\"view\",\"name\":\"立即下单\",\"url\":\"https://test.baoxianzhanggui.com/fragrance/\"},{\"type\":\"view\",\"name\":\"查看商户\",\"url\":\"https://test.baoxianzhanggui.com/fragrance/#/pages/identify/identify\"},{\"name\":\"更多\",\"sub_button\":[{\"type\":\"view\",\"name\":\"技师招募\",\"url\":\"https://test.baoxianzhanggui.com/fragrance/#/pages/join/first_join\"},{\"type\":\"view\",\"name\":\"招商合作\",\"url\":\"https://test.baoxianzhanggui.com/fragrance/#/pages/join/teamwork\"},{\"type\":\"view\",\"name\":\"投诉举报\",\"url\":\"https://test.baoxianzhanggui.com/fragrance/#/pages/join/feedback\"},{\"type\":\"view\",\"name\":\"了解我们\",\"url\":\"https://test.baoxianzhanggui.com/fragrance/#/pages/join/understand\"}]}]}"
 

+ 1 - 1
nightFragrance-admin/src/main/resources/application.yml

@@ -1,5 +1,5 @@
 spring:
   profiles:
-    active: test
+    active: dev
 #  profiles:
 #    active: druid

+ 10 - 19
nightFragrance-common/src/main/java/com/ylx/common/exception/ServiceException.java

@@ -5,8 +5,7 @@ package com.ylx.common.exception;
  *
  * @author ylx
  */
-public final class ServiceException extends RuntimeException
-{
+public final class ServiceException extends RuntimeException {
     private static final long serialVersionUID = 1L;
 
     /**
@@ -21,7 +20,7 @@ public final class ServiceException extends RuntimeException
 
     /**
      * 错误明细,内部调试错误
-     *
+     * <p>
      * 和 {@link CommonResult#getDetailMessage()} 一致的设计
      */
     private String detailMessage;
@@ -29,45 +28,37 @@ public final class ServiceException extends RuntimeException
     /**
      * 空构造方法,避免反序列化问题
      */
-    public ServiceException()
-    {
+    public ServiceException() {
     }
 
-    public ServiceException(String message)
-    {
+    public ServiceException(String message) {
         this.message = message;
     }
 
-    public ServiceException(String message, Integer code)
-    {
+    public ServiceException(String message, Integer code) {
         this.message = message;
         this.code = code;
     }
 
-    public String getDetailMessage()
-    {
+    public String getDetailMessage() {
         return detailMessage;
     }
 
     @Override
-    public String getMessage()
-    {
+    public String getMessage() {
         return message;
     }
 
-    public Integer getCode()
-    {
+    public Integer getCode() {
         return code;
     }
 
-    public ServiceException setMessage(String message)
-    {
+    public ServiceException setMessage(String message) {
         this.message = message;
         return this;
     }
 
-    public ServiceException setDetailMessage(String detailMessage)
-    {
+    public ServiceException setDetailMessage(String detailMessage) {
         this.detailMessage = detailMessage;
         return this;
     }

+ 4 - 1
nightFragrance-framework/src/main/java/com/ylx/framework/security/filter/JwtAuthenticationTokenFilter.java

@@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import com.ylx.framework.web.service.WxTokenService;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -25,6 +26,7 @@ import com.ylx.framework.web.service.TokenService;
  * @author ylx
  */
 @Component
+@Slf4j
 public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
     @Autowired
     private TokenService tokenService;
@@ -36,6 +38,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
             throws ServletException, IOException {
         if (wxTokenService.getToken(request) == null) {
+            log.info("进入PC后台用户的token验证");
             LoginUser loginUser = tokenService.getLoginUser(request);
             if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {
                 tokenService.verifyToken(loginUser);
@@ -44,7 +47,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
                 SecurityContextHolder.getContext().setAuthentication(authenticationToken);
             }
         } else {
-            System.out.println("微信用户");
+            log.info("进入微信用户的token验证");
             WxLoginUser wxUser = wxTokenService.getWxUser(request);
             if (StringUtils.isNotNull(wxUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {
                 wxTokenService.verifyToken(wxUser);

+ 1 - 1
nightFragrance-framework/src/main/java/com/ylx/framework/web/service/WxTokenService.java

@@ -167,7 +167,7 @@ public class WxTokenService {
      * 从令牌中获取数据声明
      *
      * @param token 令牌
-     * @return 数据声明
+     * @return Claims 数据声明
      */
     private Claims parseToken(String token) {
         JwtParser parser = Jwts.parser();

+ 127 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/OrderAllocationLog.java

@@ -0,0 +1,127 @@
+package com.ylx.massage.domain;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 订单分配操作记录表
+ */
+@Data
+@TableName(value = "t_order_allocation_log")
+public class OrderAllocationLog implements Serializable {
+
+
+    private static final long serialVersionUID = -5762351629641527794L;
+
+    /**
+     * 主键ID
+     */
+    private String id;
+
+    /**
+     * 订单ID
+     */
+    private String orderId;
+
+    /**
+     * 订单号
+     */
+    private String orderNo;
+
+    /**
+     * 操作类型(1:转单)
+     */
+    private Integer operationType;
+
+    /**
+     * 操作原因(转单时必填)
+     */
+    private String operationReason;
+
+    /**
+     * 原技师ID(转单时记录)
+     */
+    private String oldTechnicianId;
+
+    /**
+     * 原技师姓名
+     */
+    private String oldTechnicianName;
+
+    /**
+     * 新技师ID
+     */
+    private String newTechnicianId;
+
+    /**
+     * 新技师姓名
+     */
+    private String newTechnicianName;
+
+    /**
+     * 订单操作前状态:-1:待行数,0:待接单,1:已接单,2:已到达,3:服务中
+     */
+    private Integer orderStatusBefore;
+
+    /**
+     * 订单操作后状态
+     */
+    private Integer orderStatusAfter;
+
+    /**
+     * 操作人ID
+     */
+    private String operatorId;
+
+    /**
+     * 操作人姓名
+     */
+    private String operatorName;
+
+    /**
+     * 原技师操作前服务状态(0 可接单 1 服务中 2:不可服务)
+     */
+    private Integer oldTechnicianStatusBefore;
+
+    /**
+     * 原技师操作后服务状态(0 可接单 1 服务中 2:不可服务)
+     */
+    private Integer oldTechnicianStatusAfter;
+
+    /**
+     * 新技师操作前服务状态(0 可接单 1 服务中 2:不可服务)
+     */
+    private Integer newTechnicianStatusBefore;
+
+    /**
+     * 新技师操作后服务状态(0 可接单 1 服务中 2:不可服务)
+     */
+    private Integer newTechnicianStatusAfter;
+
+     /**
+     * 操作结果(0 分配成功 1 已取消)
+     */
+    private Integer operationResult;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    /**
+     * 更新时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+
+    /**
+     * 是否删除:0-否,1-是
+     */
+    @TableLogic
+    private Integer isDelete;
+}

+ 20 - 4
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TJs.java

@@ -109,7 +109,7 @@ public class TJs implements Serializable {
     private Integer nSex;
 
     /**
-     * 昵称
+     * 技师昵称
      * 显示给用户看的昵称
      */
     @TableField("c_nick_name")
@@ -125,7 +125,7 @@ public class TJs implements Serializable {
     private String cPortrait;
 
     /**
-     * 真实姓名
+     * 技师真实姓名
      * 技师身份证真实姓名
      */
     @TableField("c_name")
@@ -133,7 +133,7 @@ public class TJs implements Serializable {
     private String cName;
 
     /**
-     * 联系电话
+     * 技师联系电话
      * 技师手机号码
      */
     @TableField("c_phone")
@@ -205,7 +205,7 @@ public class TJs implements Serializable {
     private BigDecimal nigthMileage;
 
     /**
-     * 个人简介
+     * 技师个人简介
      * 技师自我介绍文字描述
      */
     @TableField("c_jianjie")
@@ -252,6 +252,15 @@ public class TJs implements Serializable {
     @ApiModelProperty("已服务订单数量")
     private Integer nNum;
 
+    /**
+     * 技师类型
+     * 0-真实技师(真实存在的技师)
+     * 1-虚拟技师(用于模拟服务场景)
+     */
+     @TableField("tech_type")
+    @ApiModelProperty("技师类型 0-真实技师 1-虚拟技师")
+    private Integer techType;
+
     /**
      * 服务状态
      * 0-可服务(空闲可接单)
@@ -448,6 +457,13 @@ public class TJs implements Serializable {
      @ApiModelProperty("承诺书")
     private String commitment;
 
+     /**
+     * 虚拟技师数量
+     */
+     @TableField("virtual_tech_number")
+     @ApiModelProperty("虚拟技师数量")
+    private Integer virtualTechNumber;
+
     public String getcVideo() {
         return cVideo;
     }

+ 25 - 8
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TOrder.java

@@ -75,9 +75,31 @@ public class TOrder implements Serializable {
     @ApiModelProperty("支付类型:1微信支付 2:余额支付 3:现金支付")
     private Integer payType;
 
+    /**
+     * 订单类型
+     * 0-普通订单 1-加钟订单 2-升级订单
+     */
+    @TableField("order_type")
+    @ApiModelProperty("订单类型:0普通订单,1加钟订单,2升级订单")
+    private Integer orderType;
+
+    /**
+     * 是否为虚拟技师订单
+     * 0-否 1-是
+     */
+    @TableField("virtual_order_flag")
+    @ApiModelProperty("是否为虚拟技师订单:0否,1是")
+    private Integer virtualOrderFlag;
+
+    /**
+     * 虚拟技师订单分配情况(0:真实订单 1:未分配 2:已分配)
+     */
+    @TableField("virtual_order_allocation")
+    @ApiModelProperty("虚拟技师订单分配情况(0:真实订单 1:未分配 2:已分配)")
+    private Integer virtualOrderAllocation;
+
     /**
      * 技师ID
-     * 关联的技师唯一标识
      */
     @TableField("c_js_id")
     @ApiModelProperty("技师ID")
@@ -142,13 +164,7 @@ public class TOrder implements Serializable {
     @ApiModelProperty("差价")
     private BigDecimal priceDifference;
 
-    /**
-     * 订单类型
-     * 0-普通订单 1-加钟订单 2-升级订单
-     */
-    @TableField("order_type")
-    @ApiModelProperty("订单类型:0普通订单,1加钟订单,2升级订单")
-    private Integer orderType;
+
 
     /**
      * 用户OpenID
@@ -408,6 +424,7 @@ public class TOrder implements Serializable {
     @TableLogic
     private Integer isDelete;
 
+
     /**
      * 技师信息
      * 非数据库字段,用于关联查询技师信息

+ 38 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/OrderAllocationResultVo.java

@@ -0,0 +1,38 @@
+package com.ylx.massage.domain.vo;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.util.Date;
+@Data
+public class OrderAllocationResultVo {
+
+    /**
+     * 新技师ID
+     */
+    private String newTechnicianId;
+
+    /**
+     * 新技师姓名
+     */
+    private String newTechnicianName;
+
+    /**
+     * 新技师电话
+     */
+    private String newTechnicianPhone;
+
+    /**
+     * 新技师操作后服务状态(0 可接单 1 服务中 2:不可服务)
+     */
+    private Integer newTechnicianStatusAfter;
+
+    /**
+     * 分配时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+     /**
+     * 操作结果(0 分配成功 1 已取消)
+     */
+    private Integer operationResult;
+}

+ 4 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/enums/JsStatusEnum.java

@@ -15,6 +15,10 @@ public enum JsStatusEnum {
      * 服务中
      */
     JS_SERVICE(1, "服务中"),
+
+    /**
+     * 不可服务
+     */
     JS_NO_SERVICE(2, "不可服务"),
 
     /*** 技师上岗状态*/

+ 17 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/mapper/OrderAllocationLogMapper.java

@@ -0,0 +1,17 @@
+package com.ylx.massage.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ylx.massage.domain.OrderAllocationLog;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 订单分配操作记录 Mapper 接口
+ *
+ * @author ylx
+ * @version 1.0
+ * @since 2025-01-10
+ */
+@Mapper
+public interface OrderAllocationLogMapper extends BaseMapper<OrderAllocationLog> {
+
+}

+ 73 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/OrderAllocationLogService.java

@@ -0,0 +1,73 @@
+package com.ylx.massage.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ylx.massage.domain.OrderAllocationLog;
+
+/**
+ * 订单分配操作记录服务接口
+ *
+ * @author ylx
+ * @version 1.0
+ * @since 2025-01-10
+ */
+public interface OrderAllocationLogService extends IService<OrderAllocationLog> {
+
+    /**
+     * 保存订单分配记录
+     * <p>
+     * 用于记录订单的分配、转单等操作。
+     * </p>
+     *
+     * @param allocationLog 分配记录实体
+     * @return true-保存成功,false-保存失败
+     */
+    boolean saveAllocationLog(OrderAllocationLog allocationLog);
+
+    /**
+     * 记录订单转单操作
+     * <p>
+     * 记录订单从一个技师转给另一个技师的操作,包括:
+     * - 订单状态变化
+     * - 技师状态变化
+     * - 操作人信息
+     * - 操作原因
+     * - 操作结果
+     * </p>
+     *
+     * @param orderId 订单ID
+     * @param orderNo 订单号
+     * @param oldTechnicianId 原技师ID
+     * @param oldTechnicianName 原技师姓名
+     * @param oldTechnicianStatusBefore 原技师操作前状态
+     * @param oldTechnicianStatusAfter 原技师操作后状态
+     * @param newTechnicianId 新技师ID
+     * @param newTechnicianName 新技师姓名
+     * @param newTechnicianStatusBefore 新技师操作前状态
+     * @param newTechnicianStatusAfter 新技师操作后状态
+     * @param orderStatusBefore 订单操作前状态
+     * @param orderStatusAfter 订单操作后状态
+     * @param operatorId 操作人ID
+     * @param operatorName 操作人姓名
+     * @param operationReason 操作原因
+     * @param operationResult 操作结果(0-成功,1-失败)
+     * @return true-记录成功,false-记录失败
+     */
+    boolean recordTransferOrder(
+            String orderId,
+            String orderNo,
+            String oldTechnicianId,
+            String oldTechnicianName,
+            Integer oldTechnicianStatusBefore,
+            Integer oldTechnicianStatusAfter,
+            String newTechnicianId,
+            String newTechnicianName,
+            Integer newTechnicianStatusBefore,
+            Integer newTechnicianStatusAfter,
+            Integer orderStatusBefore,
+            Integer orderStatusAfter,
+            String operatorId,
+            String operatorName,
+            String operationReason,
+            Integer operationResult
+    );
+}

+ 100 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/OrderAllocationLogServiceImpl.java

@@ -0,0 +1,100 @@
+package com.ylx.massage.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ylx.massage.domain.OrderAllocationLog;
+import com.ylx.massage.mapper.OrderAllocationLogMapper;
+import com.ylx.massage.service.OrderAllocationLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * 订单分配操作记录服务实现类
+ *
+ * @author ylx
+ * @version 1.0
+ * @since 2025-01-10
+ */
+@Service
+@Slf4j
+public class OrderAllocationLogServiceImpl extends ServiceImpl<OrderAllocationLogMapper, OrderAllocationLog>
+        implements OrderAllocationLogService {
+
+    @Override
+    public boolean saveAllocationLog(OrderAllocationLog allocationLog) {
+        if (allocationLog == null) {
+            log.error("分配记录实体为空,无法保存");
+            return false;
+        }
+        return this.save(allocationLog);
+    }
+
+    @Override
+    public boolean recordTransferOrder(
+            String orderId,
+            String orderNo,
+            String oldTechnicianId,
+            String oldTechnicianName,
+            Integer oldTechnicianStatusBefore,
+            Integer oldTechnicianStatusAfter,
+            String newTechnicianId,
+            String newTechnicianName,
+            Integer newTechnicianStatusBefore,
+            Integer newTechnicianStatusAfter,
+            Integer orderStatusBefore,
+            Integer orderStatusAfter,
+            String operatorId,
+            String operatorName,
+            String operationReason,
+            Integer operationResult
+    ) {
+        try {
+            // 创建分配记录实体
+            OrderAllocationLog allocationLog = new OrderAllocationLog();
+
+            // 基础信息
+            allocationLog.setOrderId(orderId);
+            allocationLog.setOrderNo(orderNo);
+
+            // 操作信息(转单操作类型为1)
+            allocationLog.setOperationType(1);
+            allocationLog.setOperationReason(operationReason);
+
+            // 原技师信息
+            allocationLog.setOldTechnicianId(oldTechnicianId);
+            allocationLog.setOldTechnicianName(oldTechnicianName);
+            allocationLog.setOldTechnicianStatusBefore(oldTechnicianStatusBefore);
+            allocationLog.setOldTechnicianStatusAfter(oldTechnicianStatusAfter);
+
+            // 新技师信息
+            allocationLog.setNewTechnicianId(newTechnicianId);
+            allocationLog.setNewTechnicianName(newTechnicianName);
+            allocationLog.setNewTechnicianStatusBefore(newTechnicianStatusBefore);
+            allocationLog.setNewTechnicianStatusAfter(newTechnicianStatusAfter);
+
+            // 订单状态信息
+            allocationLog.setOrderStatusBefore(orderStatusBefore);
+            allocationLog.setOrderStatusAfter(orderStatusAfter);
+
+            // 操作人信息
+            allocationLog.setOperatorId(operatorId);
+            allocationLog.setOperatorName(operatorName);
+
+            // 操作结果(0-成功,1-失败)
+            allocationLog.setOperationResult(operationResult);
+
+            // 保存记录
+            boolean result = this.save(allocationLog);
+            if (result) {
+                String resultDesc = operationResult == 0 ? "成功" : "失败";
+                log.info("成功记录转单操作 - 订单号:{}, 原技师:{}, 新技师:{}, 操作人:{}, 操作结果:{}",
+                        orderNo, oldTechnicianName, newTechnicianName, operatorName, resultDesc);
+            } else {
+                log.error("记录转单操作失败 - 订单号:{}", orderNo);
+            }
+            return result;
+        } catch (Exception e) {
+            log.error("记录转单操作异常 - 订单号:{}, 异常信息:{}", orderNo, e.getMessage(), e);
+            return false;
+        }
+    }
+}

+ 3 - 3
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/OrderValidationServiceImpl.java

@@ -256,7 +256,7 @@ public class OrderValidationServiceImpl implements OrderValidationService {
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
 
         StringBuilder message = new StringBuilder();
-        message.append("该技师当前有进行中的订单,时间冲突。\n");
+        message.append("该技师当前有进行中的订单,时间冲突。");
 
         // 现有订单信息
         message.append("现有订单:").append(existingOrder.getOrderNo()).append("\n");
@@ -271,7 +271,7 @@ public class OrderValidationServiceImpl implements OrderValidationService {
         message.append("  订单状态:").append(statusDesc).append("\n");
 
         // 新订单信息
-        message.append("新订单:").append(newOrder.getOrderNo()).append("\n");
+        message.append("新订单:").append(newOrder.getOrderNo()).append(" ");
         if (newOrder.getStartTime() != null) {
             message.append("  预计开始时间:").append(newOrder.getStartTime().format(formatter)).append("\n");
         }
@@ -282,7 +282,7 @@ public class OrderValidationServiceImpl implements OrderValidationService {
         // 提示用户
         Long durationMinutes = OrderTimeRangeUtils.calculateOrderDuration(newOrder);
         if (durationMinutes != null) {
-            message.append("\n建议:请等待当前订单完成后再试,或选择其他时间段(需间隔至少30分钟)。");
+            message.append(" 建议:请等待当前订单完成后再试,或选择其他时间段(需间隔至少30分钟)。");
         }
 
         return message.toString();

+ 113 - 6
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TJsServiceImpl.java

@@ -21,6 +21,7 @@ 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 lombok.extern.slf4j.Slf4j;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -72,6 +73,9 @@ public class TJsServiceImpl extends ServiceImpl<TJsMapper, TJs> implements TJsSe
     @Resource(name = "commonAsyncExecutor")
     private ThreadPoolTaskExecutor threadPoolTaskExecutor;
 
+    @Resource
+    private VirtualTechnicianNicknameGenerator nicknameGenerator;
+
 
     @Override
     public Page<TJs> getAll(Page<TJs> page, TJsVo param) {
@@ -318,6 +322,7 @@ public class TJsServiceImpl extends ServiceImpl<TJsMapper, TJs> implements TJsSe
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public boolean pcAddJs(TJs js) {
         /*if (js.getCity() == null) {
             throw new ServiceException("所在城市不能为空");
@@ -327,17 +332,24 @@ public class TJsServiceImpl extends ServiceImpl<TJsMapper, TJs> implements TJsSe
             throw new ServiceException("虚拟订单数量不能为空");
         }
 
-        // 评分默认最高
+        if (js.getVirtualTechNumber() == null) {
+            // 默认值为10
+            js.setVirtualTechNumber(10);
+        }
+
+        // 验证虚拟技师数量
+        Integer virtualTechNumber = js.getVirtualTechNumber();
+        if (virtualTechNumber < 0) {
+            throw new ServiceException("虚拟技师数量不能为负数");
+        }
+
+        // 保存真实技师(techType = 0)
+        js.setTechType(0); // 0-真实技师
         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_PASS.getCode());
         js.setnB1(MassageConstants.INTEGER_ZERO);
         js.setnB2(MassageConstants.INTEGER_ZERO);
@@ -345,6 +357,13 @@ public class TJsServiceImpl extends ServiceImpl<TJsMapper, TJs> implements TJsSe
         js.setDtCreateTime(LocalDateTime.now());
 
         this.save(js);
+
+        // 如果需要生成虚拟技师
+        if (virtualTechNumber > 0) {
+            log.info("开始为真实技师[{}]生成{}个虚拟技师", js.getcName(), virtualTechNumber);
+            generateVirtualTechnicians(js, virtualTechNumber);
+        }
+
         /*String token = weChatUtil.getToken();
         //获取的二维码ticket
         Map<?, ?> jsTicket = weChatUtil.getJsTicket(token, js.getId());
@@ -352,6 +371,94 @@ public class TJsServiceImpl extends ServiceImpl<TJsMapper, TJs> implements TJsSe
         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.setnNum(MassageConstants.INTEGER_ZERO); // 已服务数量
+                virtualJs.setnStatus(JsStatusEnum.JS_NO_SERVICE.getCode()); // 服务状态
+                virtualJs.setnStatus2(JsStatusEnum.POST_NOT_ON_DUTY.getCode()); // 上岗状态
+                virtualJs.setnTong(JsStatusEnum.JS_PASS.getCode()); // 审核状态
+                virtualJs.setnB1(MassageConstants.INTEGER_ZERO);
+                virtualJs.setnB2(MassageConstants.INTEGER_ZERO);
+                virtualJs.setnB3(MassageConstants.INTEGER_ZERO);
+                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()) {

+ 189 - 25
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TOrderServiceImpl.java

@@ -13,6 +13,7 @@ import com.ylx.common.config.WechatAccountConfig;
 import com.ylx.common.constant.MassageConstants;
 import com.ylx.common.core.domain.R;
 import com.ylx.common.exception.ServiceException;
+import com.ylx.common.utils.SecurityUtils;
 import com.ylx.massage.domain.*;
 import com.ylx.massage.domain.vo.CouponReceiveVo;
 import com.ylx.massage.domain.vo.HomeBlock;
@@ -94,6 +95,9 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
     @Resource
     private OrderNotificationService orderNotificationService;
 
+    @Resource
+    private OrderAllocationLogService allocationLogService;
+
     /**
      * 判断是否免车费
      * 时间段判断:
@@ -135,14 +139,32 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
     @Override
     @Transactional(rollbackFor = Exception.class)
     public TOrder addOrder(TOrder order) {
+        String jsId = order.getcJsId();
         // 1. 基础参数校验
-        if (StringUtils.isBlank(order.getcJsId())) {
+        if (StringUtils.isBlank(jsId)) {
             throw new ServiceException("请选择技师");
         }
         if (order.getcGoods().isEmpty()) {
             throw new ServiceException("请选择项目");
         }
 
+        TJs js = jsService.getById(jsId);
+        if (js == null) {
+            throw new ServiceException("技师不存在");
+        }
+        Integer techType = js.getTechType();
+        // 虚拟技师
+        if(techType.equals(1)){
+            //虚拟技师订单
+            order.setVirtualOrderFlag(1);
+            //虚拟技师订单未分配
+            order.setVirtualOrderAllocation(1);
+        }else{
+            //真实订单
+            order.setVirtualOrderFlag(0);
+            order.setVirtualOrderAllocation(0);
+        }
+
         // 2. 【新增】订单状态锁校验 - 检查技师是否可以接单
         // 确保技师没有进行中的订单,保证服务时间互斥
         log.info("开始校验技师 {} 是否可以接单,订单号:{}", order.getcJsId(), order.getOrderNo());
@@ -153,7 +175,7 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
 //        List<CouponReceiveVo> coupons = couponReceiveService.getByOpenId(order.getcOpenId());
 //        BigDecimal preferential = this.setCoupon(coupons);
 //        order.setPreferential(preferential);
-
+        // 生成订单号
         order.setOrderNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_ORDER));
         //订单价格
         List<TXiangmu> list = JSONObject.parseArray(order.getcGoods().toJSONString(), TXiangmu.class);
@@ -166,7 +188,6 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
         if (address == null) {
             throw new ServiceException("请先添加地址");
         }
-        TJs js = jsService.getById(order.getcJsId());
 
         //添加技师位置信息
         locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
@@ -306,34 +327,177 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
     @Override
     @Transactional(rollbackFor = Exception.class)
     public TOrder transferOrder(TOrder order) {
+        // ========== 第1步:参数校验 ==========
         if (StringUtils.isBlank(order.getcId())) {
             throw new ServiceException("订单id不能为空");
         }
         if (StringUtils.isBlank(order.getcJsId())) {
             throw new ServiceException("转单技师ID不能为空");
         }
-        TOrder oldOrder = this.getById(order.getcId());
-
-        oldOrder.setOldJsId(oldOrder.getcJsId());
-        oldOrder.setcJsId(order.getcJsId());
-        log.info("新技师:{},老技师{}", oldOrder.getcJsId(), oldOrder.getOldJsId());
-        if (!this.updateById(oldOrder)) {
-            throw new ServiceException("转单失败");
-        }
-        //改变新技师服务状态
-        TJs tJs = new TJs();
-        tJs.setId(oldOrder.getcJsId());
-        tJs.setnStatus(JsStatusEnum.JS_SERVICE.getCode());
-        jsService.updateById(tJs);
-        //改变旧技师服务状态
-        TJs oldTJs = new TJs();
-        oldTJs.setId(oldOrder.getOldJsId());
-        oldTJs.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
-        jsService.updateById(oldTJs);
-
-        //通知技师转单
-        this.newOrderNotification(oldOrder);
-        return oldOrder;
+
+        // 定义操作结果(默认为失败)
+        Integer operationResult = 1; // 1-失败
+
+        // 定义操作记录所需的变量
+        String orderId = null;
+        String orderNo = null;
+        String oldTechnicianId = null;
+        String oldTechnicianName = null;
+        Integer oldTechnicianStatusBefore = null;
+        Integer oldTechnicianStatusAfter = null;
+        String newTechnicianId = null;
+        String newTechnicianName = null;
+        Integer newTechnicianStatusBefore = null;
+        Integer newTechnicianStatusAfter = null;
+        Integer orderStatusBefore = null;
+        Integer orderStatusAfter = null;
+        String operatorId = null;
+        String operatorName = null;
+        String operationReason = "虚拟订单分配";
+
+        try {
+            // ========== 第2步:查询原订单信息 ==========
+            TOrder oldOrder = this.getById(order.getcId());
+            if (oldOrder == null) {
+                throw new ServiceException("订单不存在");
+            }
+
+            //原技师ID
+            oldTechnicianId = oldOrder.getcJsId();
+            //新技师ID
+            newTechnicianId = order.getcJsId();
+
+            // 记录订单操作前状态
+            orderStatusBefore = oldOrder.getnStatus();
+            orderId = oldOrder.getcId();
+            orderNo = oldOrder.getOrderNo();
+
+            log.info("开始转单操作 - 订单号:{}, 原技师ID:{}, 新技师ID:{}, 订单状态:{}", oldOrder.getOrderNo(), oldTechnicianId, newTechnicianId, orderStatusBefore);
+
+            // ========== 第3步:查询原技师信息 ==========
+            TJs oldTechnician = jsService.getById(oldTechnicianId);
+            if (oldTechnician == null) {
+                throw new ServiceException("原技师不存在");
+            }
+            oldTechnicianName = oldTechnician.getcName();
+            oldTechnicianStatusBefore = oldTechnician.getnStatus();
+
+            // ========== 第4步:查询新技师信息 ==========
+            TJs newTechnician = jsService.getById(newTechnicianId);
+            if (newTechnician == null) {
+                throw new ServiceException("新技师不存在");
+            }
+            newTechnicianName = newTechnician.getcName();
+            newTechnicianStatusBefore = newTechnician.getnStatus();
+
+            // ========== 第5步:更新订单技师信息 ==========
+            oldOrder.setOldJsId(oldTechnicianId);  // 保存原技师ID
+            oldOrder.setcJsId(newTechnicianId);      // 更新为新技师ID
+
+            log.info("更新订单技师 - 订单号:{}, 原技师:[ID:{}, 姓名:{}], 新技师:[ID:{}, 姓名:{}]", oldOrder.getOrderNo(), oldTechnicianId, oldTechnicianName, newTechnicianId, newTechnicianName);
+
+            if (!this.updateById(oldOrder)) {
+                throw new ServiceException("转单失败:更新订单技师信息失败");
+            }
+
+            // 记录订单操作后状态(转单后状态通常保持不变)
+            orderStatusAfter = oldOrder.getnStatus();
+
+            // ========== 第6步:更新新技师状态(可服务 → 服务中)==========
+            TJs newJsUpdate = new TJs();
+            newJsUpdate.setId(newTechnicianId);
+            newJsUpdate.setnStatus(JsStatusEnum.JS_SERVICE.getCode());
+            if (!jsService.updateById(newJsUpdate)) {
+                throw new ServiceException("转单失败:更新新技师状态失败");
+            }
+            newTechnicianStatusAfter = JsStatusEnum.JS_SERVICE.getCode();
+
+            // ========== 第7步:更新原技师状态(服务中 → 可服务)==========
+            TJs oldJsUpdate = new TJs();
+            oldJsUpdate.setId(oldTechnicianId);
+            oldJsUpdate.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
+            if (!jsService.updateById(oldJsUpdate)) {
+                throw new ServiceException("转单失败:更新原技师状态失败");
+            }
+            oldTechnicianStatusAfter = JsStatusEnum.JS_SERVICEABLE.getCode();
+
+            log.info("更新技师状态完成 - 新技师:{} {}→{}, 原技师:{} {}→{}",
+                newTechnicianName, getStatusName(newTechnicianStatusBefore), getStatusName(newTechnicianStatusAfter),
+                oldTechnicianName, getStatusName(oldTechnicianStatusBefore), getStatusName(oldTechnicianStatusAfter));
+
+            // ========== 第8步:获取操作人信息 ==========
+            operatorId = SecurityUtils.getUserId() != null ? SecurityUtils.getUserId().toString() : "ADMIN";
+            operatorName = SecurityUtils.getUsername() != null ? SecurityUtils.getUsername() : "系统管理员";
+
+            // ========== 第9步:转单成功,设置操作结果为成功 ==========
+            operationResult = 0; // 0-成功
+            log.info("转单操作完成 - 订单号:{}", oldOrder.getOrderNo());
+            return oldOrder;
+        } catch (ServiceException e) {
+            // 业务异常,操作失败
+            log.error("转单操作失败 - 订单号:{}, 错误信息:{}", orderNo, e.getMessage());
+            operationResult = 1; // 1-失败
+            throw e;
+
+        } catch (Exception e) {
+            // 系统异常,操作失败
+            log.error("转单操作异常 - 订单号:{}, 异常信息:{}", orderNo, e.getMessage(), e);
+            operationResult = 1; // 1-失败
+            throw new ServiceException("转单操作异常:" + e.getMessage());
+        } finally {
+            // ========== 第10步:记录转单操作日志(无论成功或失败都记录)==========
+            try {
+                // 只有在获取到基本信息后才记录日志
+                if (orderId != null && orderNo != null && oldTechnicianId != null && newTechnicianId != null) {
+                    allocationLogService.recordTransferOrder(
+                            orderId,                     // orderId
+                            orderNo,                     // orderNo
+                            oldTechnicianId,             // oldTechnicianId
+                            oldTechnicianName,           // oldTechnicianName
+                            oldTechnicianStatusBefore,   // oldTechnicianStatusBefore
+                            oldTechnicianStatusAfter,    // oldTechnicianStatusAfter
+                            newTechnicianId,             // newTechnicianId
+                            newTechnicianName,           // newTechnicianName
+                            newTechnicianStatusBefore,   // newTechnicianStatusBefore
+                            newTechnicianStatusAfter,    // newTechnicianStatusAfter
+                            orderStatusBefore,           // orderStatusBefore
+                            orderStatusAfter,            // orderStatusAfter
+                            operatorId,                  // operatorId
+                            operatorName,                // operatorName
+                            operationReason,             // operationReason
+                            operationResult              // operationResult(0-成功,1-失败)
+                    );
+
+                    String resultDesc = operationResult == 0 ? "成功" : "失败";
+                    log.info("转单操作记录已保存 - 订单号:{}, 操作结果:{}", orderNo, resultDesc);
+                }
+            } catch (Exception e) {
+                // 记录日志失败不影响转单操作
+                log.error("记录转单操作日志失败 - 订单号:{}, 错误信息:{}", orderNo, e.getMessage(), e);
+            }
+        }
+    }
+
+    /**
+     * 获取技师状态名称
+     *
+     * @param status 状态码
+     * @return String 状态名称
+     */
+    private String getStatusName(Integer status) {
+        if (status == null) {
+            return "未知";
+        }
+        switch (status) {
+            case 0:
+                return "可服务";
+            case 1:
+                return "服务中";
+            case 2:
+                return "不可服务";
+            default:
+                return "未知(" + status + ")";
+        }
     }
 
     @Override

+ 4 - 2
nightFragrance-massage/src/main/java/com/ylx/massage/task/massageTask.java

@@ -66,11 +66,13 @@ public class massageTask {
     @Resource
     private WeChatUtil weChatUtil;
 
-
+    /**
+     * 取消超时未支付订单
+     */
     public void cancelOrder() {
 
         Date nowDate = new Date();
-        log.info("开始执行取消订单任务当前时间,{}", nowDate);
+        log.info("开始执行取消订单任务当前时间{}", nowDate);
 
         Date date = DateTimeUtils.addMinute(nowDate, -5);
         log.info("开始执行取消订单任务当前时间减5分钟,{}", date);

+ 253 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/utils/VirtualTechnicianNicknameGenerator.java

@@ -0,0 +1,253 @@
+package com.ylx.massage.utils;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ylx.common.exception.ServiceException;
+import com.ylx.massage.domain.TJs;
+import com.ylx.massage.service.TJsService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
+
+/**
+ * 虚拟技师昵称生成器
+ * <p>
+ * 生成2-3个汉字的偏女性化昵称,确保全表唯一。
+ * 使用常用姓氏和女性化名字组合,支持随机生成。
+ * </p>
+ *
+ * @author ylx
+ * @version 1.0
+ * @since 2025-01-09
+ */
+@Slf4j
+@Component
+public class VirtualTechnicianNicknameGenerator {
+
+    @Resource
+    private TJsService tJsService;
+
+    private static VirtualTechnicianNicknameGenerator instance;
+
+    @PostConstruct
+    public void init() {
+        instance = this;
+    }
+
+    /**
+     * 常用姓氏(前50个)
+     */
+    private static final String[] SURNAMES = {
+            "王", "李", "张", "刘", "陈", "杨", "黄", "赵", "周", "吴",
+            "徐", "孙", "马", "胡", "朱", "郭", "何", "罗", "高", "林",
+            "梁", "郑", "谢", "宋", "唐", "许", "韩", "冯", "邓", "曹",
+            "彭", "曾", "萧", "田", "董", "袁", "潘", "于", "蒋", "蔡",
+            "余", "杜", "叶", "程", "苏", "魏", "吕", "丁", "任", "沈"
+    };
+
+    /**
+     * 女性化常用字(用于名字)
+     */
+    private static final String[] FEMALE_CHARACTERS = {
+            // 单字名(2字昵称:姓氏+单字)
+            "婷", "雪", "慧", "颖", "雅", "静", "雯", "婷", "欣", "怡",
+            "娜", "丹", "莉", "娟", "秀", "英", "华", "玉", "梅", "珍",
+            "芳", "红", "敏", "霞", "丽", "桂", "琼", "琴", "兰", "平",
+            "燕", "艳", "玲", "薇", "萍", "芬", "菊", "云", "莲", "珠",
+            "文", "月", "凤", "宝", "金", "荣", "爱", "翠", "巧", "娴",
+
+            // 双字名(3字昵称:姓氏+双字)
+            // 第一字
+            "小", "晓", "雨", "梦", "佳", "诗", "春", "夏", "秋", "冬",
+            "紫", "子", "思", "嘉", "若", "如", "语", "妙", "玉", "可",
+
+            // 第二字
+            "萱", "璇", "瑶", "琪", "琳", "瑗", "瑛", "蓉", "蓓", "蕾",
+            "薇", "蕊", "菲", "芳", "芸", "芝", "兰", "荷", "菊", "梅",
+            "竹", "月", "雪", "云", "霞", "露", "霜", "冰", "清", "洁"
+    };
+
+    /**
+     * 缓存已存在的昵称集合,避免频繁查询数据库
+     */
+    private Set<String> existingNicknames = null;
+
+    /**
+     * 生成唯一的虚拟技师昵称
+     * <p>
+     * 业务规则:
+     * <ul>
+     *   <li>生成长度为2-3个汉字的昵称</li>
+     *   <li>偏女性化名字风格</li>
+     *   <li>确保全表唯一(不与现有技师昵称重复)</li>
+     *   <li>最多尝试1000次,避免无限循环</li>
+     * </ul>
+     * </p>
+     *
+     * @return 唯一的虚拟技师昵称
+     * @throws ServiceException 当无法生成唯一昵称时抛出异常
+     */
+    public String generateUniqueNickname() {
+        // 初始化已存在昵称缓存(懒加载)
+        if (existingNicknames == null) {
+            loadExistingNicknames();
+        }
+
+        // 最多尝试1000次
+        int maxAttempts = 1000;
+        for (int i = 0; i < maxAttempts; i++) {
+            String nickname = generateRandomNickname();
+
+            // 检查是否唯一
+            if (!existingNicknames.contains(nickname)) {
+                // 添加到缓存,防止本次批量操作中重复
+                existingNicknames.add(nickname);
+                log.debug("成功生成唯一昵称:{} (尝试次数:{})", nickname, i + 1);
+                return nickname;
+            }
+        }
+
+        throw new ServiceException("生成唯一昵称失败:已达到最大尝试次数(" + maxAttempts + "),请稍后重试或联系管理员");
+    }
+
+    /**
+     * 批量生成唯一的虚拟技师昵称
+     *
+     * @param count 需要生成的昵称数量
+     * @return List<String> 昵称列表
+     * @throws ServiceException 当无法生成足够数量的唯一昵称时抛出异常
+     */
+    public List<String> batchGenerateUniqueNicknames(int count) {
+        if (count <= 0) {
+            throw new ServiceException("生成数量必须大于0");
+        }
+
+        List<String> nicknames = new ArrayList<>(count);
+        Set<String> generatedInThisBatch = new HashSet<>();
+
+        // 初始化已存在昵称缓存
+        if (existingNicknames == null) {
+            loadExistingNicknames();
+        }
+
+        // 逐个生成昵称,确保不重复
+        int maxAttempts = count * 100; // 每个昵称最多尝试100次
+        int attempts = 0;
+
+        while (generatedInThisBatch.size() < count && attempts < maxAttempts) {
+            String nickname = generateRandomNickname();
+
+            // 检查是否在数据库中已存在
+            if (!existingNicknames.contains(nickname) && !generatedInThisBatch.contains(nickname)) {
+                generatedInThisBatch.add(nickname);
+                nicknames.add(nickname);
+                // 添加到全局缓存
+                existingNicknames.add(nickname);
+            }
+
+            attempts++;
+        }
+
+        if (nicknames.size() < count) {
+            throw new ServiceException("批量生成昵称失败:期望生成" + count + "个,实际生成" + nicknames.size() + "个。请减少生成数量或联系管理员");
+        }
+
+        log.info("成功批量生成{}个唯一昵称", nicknames.size());
+        return nicknames;
+    }
+
+    /**
+     * 生成随机昵称(不保证唯一性)
+     * <p>
+     * 生成规则:
+     * <ul>
+     *   <li>50%概率生成2字昵称:姓氏 + 单字名</li>
+     *   <li>50%概率生成3字昵称:姓氏 + 双字名</li>
+     * </ul>
+     * </p>
+     *
+     * @return String 随机生成的昵称
+     */
+    private String generateRandomNickname() {
+        ThreadLocalRandom random = ThreadLocalRandom.current();
+
+        // 随机选择姓氏
+        String surname = SURNAMES[random.nextInt(SURNAMES.length)];
+
+        // 50%概率生成2字昵称,50%概率生成3字昵称
+        if (random.nextBoolean()) {
+            // 2字昵称:姓氏 + 单字名
+            String name = FEMALE_CHARACTERS[random.nextInt(50)]; // 使用前50个单字
+            return surname + name;
+        } else {
+            // 3字昵称:姓氏 + 双字名
+            String firstChar = FEMALE_CHARACTERS[50 + random.nextInt(20)]; // 第51-70个为第一字
+            String secondChar = FEMALE_CHARACTERS[70 + random.nextInt(30)]; // 第71-100个为第二字
+            return surname + firstChar + secondChar;
+        }
+    }
+
+    /**
+     * 从数据库加载所有已存在的昵称
+     */
+    private void loadExistingNicknames() {
+        log.info("开始加载现有技师昵称数据...");
+        try {
+            // 查询所有技师的昵称
+            LambdaQueryWrapper<TJs> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.select(TJs::getcNickName);
+            queryWrapper.isNotNull(TJs::getcNickName);
+
+            List<TJs> allJs = tJsService.list(queryWrapper);
+            existingNicknames = allJs.stream()
+                    .map(TJs::getcNickName)
+                    .filter(Objects::nonNull)
+                    .filter(nickname -> !nickname.trim().isEmpty())
+                    .collect(Collectors.toSet());
+
+            log.info("成功加载{}个现有技师昵称", existingNicknames.size());
+        } catch (Exception e) {
+            log.error("加载现有昵称数据失败", e);
+            throw new ServiceException("加载现有昵称数据失败,请稍后重试");
+        }
+    }
+
+    /**
+     * 清空昵称缓存
+     * <p>
+     * 用于批量操作完成后清理缓存,释放内存。
+     * </p>
+     */
+    public void clearCache() {
+        if (existingNicknames != null) {
+            existingNicknames.clear();
+            existingNicknames = null;
+            log.info("昵称缓存已清空");
+        }
+    }
+
+    /**
+     * 刷新昵称缓存
+     * <p>
+     * 重新从数据库加载最新昵称数据。
+     * </p>
+     */
+    public void refreshCache() {
+        clearCache();
+        loadExistingNicknames();
+        log.info("昵称缓存已刷新");
+    }
+
+    /**
+     * 获取已缓存的昵称数量(用于监控)
+     *
+     * @return 缓存的昵称数量
+     */
+    public int getCachedNicknameCount() {
+        return existingNicknames == null ? 0 : existingNicknames.size();
+    }
+}

+ 5 - 0
nightFragrance-massage/src/main/resources/mapper/massage/TOrderMapper.xml

@@ -8,6 +8,8 @@
         <result column="pay_type" property="payType"/>
         <result column="preferential" property="preferential"/>
         <result column="order_type" property="orderType"/>
+        <result column="virtual_order_flag" property="virtualOrderFlag"/>
+        <result column="virtual_order_allocation" property="virtualOrderAllocation"/>
         <result column="price_difference" property="priceDifference"/>
         <result column="c_js_id" property="cJsId"/>
         <result column="tape" property="tape"/>
@@ -82,6 +84,7 @@
         </association>
     </resultMap>
 
+    <!-- 查询所有订单(PC端) -->
     <select id="getAll" resultMap="getAllMap">
         select t_order.c_id,
         t_order.order_no,
@@ -89,6 +92,8 @@
         t_order.pay_type,
         t_order.c_js_id,
         t_order.order_type,
+        t_order.virtual_order_flag,
+        t_order.virtual_order_allocation,
         t_order.price_difference,
         t_order.tape,
         t_order.fare,

+ 22 - 52
nightFragrance-quartz/src/main/java/com/ylx/quartz/controller/SysJobController.java

@@ -2,6 +2,7 @@ package com.ylx.quartz.controller;
 
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
+
 import org.quartz.SchedulerException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -34,8 +35,7 @@ import com.ylx.quartz.util.ScheduleUtils;
  */
 @RestController
 @RequestMapping("/monitor/job")
-public class SysJobController extends BaseController
-{
+public class SysJobController extends BaseController {
     @Autowired
     private ISysJobService jobService;
 
@@ -44,8 +44,7 @@ public class SysJobController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('monitor:job:list')")
     @GetMapping("/list")
-    public TableDataInfo list(SysJob sysJob)
-    {
+    public TableDataInfo list(SysJob sysJob) {
         startPage();
         List<SysJob> list = jobService.selectJobList(sysJob);
         return getDataTable(list);
@@ -57,8 +56,7 @@ public class SysJobController extends BaseController
     @PreAuthorize("@ss.hasPermi('monitor:job:export')")
     @Log(title = "定时任务", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
-    public void export(HttpServletResponse response, SysJob sysJob)
-    {
+    public void export(HttpServletResponse response, SysJob sysJob) {
         List<SysJob> list = jobService.selectJobList(sysJob);
         ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
         util.exportExcel(response, list, "定时任务");
@@ -69,8 +67,7 @@ public class SysJobController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('monitor:job:query')")
     @GetMapping(value = "/{jobId}")
-    public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
-    {
+    public AjaxResult getInfo(@PathVariable("jobId") Long jobId) {
         return success(jobService.selectJobById(jobId));
     }
 
@@ -80,30 +77,18 @@ public class SysJobController extends BaseController
     @PreAuthorize("@ss.hasPermi('monitor:job:add')")
     @Log(title = "定时任务", businessType = BusinessType.INSERT)
     @PostMapping
-    public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException
-    {
-        if (!CronUtils.isValid(job.getCronExpression()))
-        {
+    public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException {
+        if (!CronUtils.isValid(job.getCronExpression())) {
             return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确");
-        }
-        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
-        {
+        } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) {
             return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
-        }
-        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS }))
-        {
+        } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS})) {
             return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
-        }
-        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
-        {
+        } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.HTTP, Constants.HTTPS})) {
             return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
-        }
-        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
-        {
+        } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) {
             return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规");
-        }
-        else if (!ScheduleUtils.whiteList(job.getInvokeTarget()))
-        {
+        } else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) {
             return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
         }
         job.setCreateBy(getUsername());
@@ -116,30 +101,18 @@ public class SysJobController extends BaseController
     @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
     @Log(title = "定时任务", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException
-    {
-        if (!CronUtils.isValid(job.getCronExpression()))
-        {
+    public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException {
+        if (!CronUtils.isValid(job.getCronExpression())) {
             return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确");
-        }
-        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
-        {
+        } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) {
             return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
-        }
-        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS }))
-        {
+        } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS})) {
             return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
-        }
-        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
-        {
+        } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.HTTP, Constants.HTTPS})) {
             return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
-        }
-        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
-        {
+        } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) {
             return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规");
-        }
-        else if (!ScheduleUtils.whiteList(job.getInvokeTarget()))
-        {
+        } else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) {
             return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
         }
         job.setUpdateBy(getUsername());
@@ -152,8 +125,7 @@ public class SysJobController extends BaseController
     @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
     @Log(title = "定时任务", businessType = BusinessType.UPDATE)
     @PutMapping("/changeStatus")
-    public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException
-    {
+    public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException {
         SysJob newJob = jobService.selectJobById(job.getJobId());
         newJob.setStatus(job.getStatus());
         return toAjax(jobService.changeStatus(newJob));
@@ -165,8 +137,7 @@ public class SysJobController extends BaseController
     @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
     @Log(title = "定时任务", businessType = BusinessType.UPDATE)
     @PutMapping("/run")
-    public AjaxResult run(@RequestBody SysJob job) throws SchedulerException
-    {
+    public AjaxResult run(@RequestBody SysJob job) throws SchedulerException {
         boolean result = jobService.run(job);
         return result ? success() : error("任务不存在或已过期!");
     }
@@ -177,8 +148,7 @@ public class SysJobController extends BaseController
     @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
     @Log(title = "定时任务", businessType = BusinessType.DELETE)
     @DeleteMapping("/{jobIds}")
-    public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
-    {
+    public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException {
         jobService.deleteJobByIds(jobIds);
         return success();
     }