Преглед изворни кода

开发动态相关的接口

jinshihui пре 1 недеља
родитељ
комит
c46db0ef55
23 измењених фајлова са 992 додато и 298 уклоњено
  1. 131 135
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/MaTechnicianController.java
  2. 46 2
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TechnicianMomentController.java
  3. 1 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/MaTechnician.java
  4. 23 17
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TechnicianMoment.java
  5. 0 7
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/EditMomentDTO.java
  6. 2 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MerchantApplyFileDto.java
  7. 4 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MerchantApplyFileRequestDto.java
  8. 0 1
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MomentAuditDTO.java
  9. 22 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MomentListStatusDTO.java
  10. 22 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MomentRecommendDTO.java
  11. 50 4
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaTechnicianCertificateVO.java
  12. 12 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MomentManageVO.java
  13. 27 22
      nightFragrance-massage/src/main/java/com/ylx/massage/enums/FileTypeEnum.java
  14. 8 6
      nightFragrance-massage/src/main/java/com/ylx/massage/mapper/MaTechnicianMapper.java
  15. 10 0
      nightFragrance-massage/src/main/java/com/ylx/massage/mapper/TechnicianMomentMapper.java
  16. 5 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/IMaTechnicianService.java
  17. 20 3
      nightFragrance-massage/src/main/java/com/ylx/massage/service/ITechnicianMomentService.java
  18. 122 65
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/MaTechnicianServiceImpl.java
  19. 74 35
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TechnicianMomentServiceImpl.java
  20. 32 1
      nightFragrance-massage/src/main/resources/mapper/massage/TechnicianMomentMapper.xml
  21. 82 0
      nightFragrance-massage/src/test/java/com/ylx/massage/mapper/TechnicianMomentMapperXmlTest.java
  22. 191 0
      nightFragrance-massage/src/test/java/com/ylx/massage/service/impl/MaTechnicianServiceImplTest.java
  23. 108 0
      nightFragrance-massage/src/test/java/com/ylx/massage/service/impl/TechnicianMomentServiceImplTest.java

+ 131 - 135
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/MaTechnicianController.java

@@ -1,13 +1,14 @@
 package com.ylx.web.controller.massage;
 
-import java.util.*;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Pattern;
 import javax.annotation.Resource;
-import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
 
-import cn.hutool.json.JSONObject;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -16,20 +17,16 @@ import com.ylx.common.annotation.Log;
 import com.ylx.common.constant.CacheConstants;
 import com.ylx.common.constant.Constants;
 import com.ylx.common.core.controller.BaseController;
-import com.ylx.common.core.domain.AjaxResult;
 import com.ylx.common.core.domain.R;
 import com.ylx.common.core.domain.model.LoginUser;
 import com.ylx.common.core.domain.model.WxLoginUser;
-import com.ylx.common.core.domain.model.aliyun.SMSVerificationCode;
 import com.ylx.common.core.domain.model.aliyun.SendSmsComponents;
-import com.ylx.common.core.domain.model.aliyun.SendSmsEnum;
 import com.ylx.common.core.page.TableDataInfo;
 import com.ylx.common.core.redis.RedisCache;
 import com.ylx.common.enums.BusinessType;
 import com.ylx.common.exception.user.CaptchaException;
 import com.ylx.common.exception.user.CaptchaExpireException;
 import com.ylx.common.utils.MessageUtils;
-import com.ylx.common.utils.SecurityUtils;
 import com.ylx.common.utils.StringUtils;
 import com.ylx.common.utils.poi.ExcelUtil;
 import com.ylx.framework.manager.AsyncManager;
@@ -43,7 +40,6 @@ import com.ylx.massage.domain.vo.*;
 import com.ylx.massage.service.IMaProjectService;
 import com.ylx.massage.service.IMaTechnicianService;
 import com.ylx.massage.service.TWxUserService;
-import com.ylx.project.domain.Project;
 import com.ylx.servicecategory.domain.ServiceCategory;
 import com.ylx.servicecategory.service.ServiceCategoryService;
 import com.ylx.system.service.ISysConfigService;
@@ -58,26 +54,6 @@ import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.ObjectUtils;
 import org.springframework.web.bind.annotation.*;
-import com.ylx.common.annotation.Log;
-import com.ylx.common.core.controller.BaseController;
-import com.ylx.common.core.domain.AjaxResult;
-import com.ylx.common.enums.BusinessType;
-import com.ylx.massage.domain.MaTechnician;
-import com.ylx.massage.service.IMaTechnicianService;
-import com.ylx.common.utils.poi.ExcelUtil;
-import com.ylx.common.core.page.TableDataInfo;
-import org.springframework.web.multipart.MultipartFile;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.validation.Valid;
-import java.util.List;
-import java.util.Objects;
-import java.util.Random;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Pattern;
-
-import static jdk.nashorn.internal.runtime.regexp.joni.Config.log;
 
 /**
  * 技师Controller
@@ -124,18 +100,15 @@ public class MaTechnicianController extends BaseController {
      * 商户手机号密码登录
      *
      * @param thirdPartyLoginsVo
-     * @return Result<WxLoginUser>
+     * @return R<WxLoginUser>
      */
     @ApiOperation(value = "商户手机号密码登录", notes = "商户手机号密码登录")
     @PostMapping(value = "/clientLogin")
     @Transactional
-    public Result<WxLoginUser> login(@RequestBody ThirdPartyLoginsVo thirdPartyLoginsVo) throws Exception {
-        // 获取登录用户信息
-        Result<WxLoginUser> result = new Result<>();
-
+    public R<WxLoginUser> login(@RequestBody ThirdPartyLoginsVo thirdPartyLoginsVo) throws Exception {
         // 校验手机号是否为空
         if (StringUtils.isBlank(thirdPartyLoginsVo.getPhone())) {
-            return result.error500("请输入手机号");
+            return R.fail("请输入手机号");
         }
 
         // 校验用户是否存在且有效
@@ -145,25 +118,25 @@ public class MaTechnicianController extends BaseController {
         TWxUser tWxUser = wxUserService.getOne(queryWrapper);
         // 校验用户是否有效
         if (ObjectUtils.isEmpty(tWxUser)) {
-            return result.error500("商户不存在,请先注册");
+            return R.fail("商户不存在,请先注册");
         }
 
         if (thirdPartyLoginsVo.getCodeSwitch()) {
             // 短信验证
             String msg = redisTemplate.opsForValue().get(PHONE_THREEUSERPARTCLIENT_CODE_KEY + thirdPartyLoginsVo.getPhone());
             if (StringUtils.isEmpty(msg)) {
-                return Result.error("验证码已失效");
+                return R.fail("验证码已失效");
             }
 
             if (!thirdPartyLoginsVo.getPhoneMsg().equals(msg)) {
-                return Result.error("短信验证码不正确");
+                return R.fail("短信验证码不正确");
             }
         } else {
             // 初始化加密工具
             // 验证:比对是否匹配
             boolean isOk = passwordEncoder.matches(thirdPartyLoginsVo.getPassWord(), tWxUser.getCPassword());
             /*if (!isOk) {
-                return Result.error("密码错误");
+                return R.fail("密码错误");
             }*/
         }
         WxLoginUser wxUser = new WxLoginUser();
@@ -172,7 +145,7 @@ public class MaTechnicianController extends BaseController {
         String token = wxTokenService.createToken(wxUser);
         log.info("token的值:{}", token);
         if (token == null || token.isEmpty()) {
-            return Result.error("生成令牌失败");
+            return R.fail("生成令牌失败");
         }
         //给我把token的值保存到redis中
         redisTemplate.opsForValue().set(wxUser.getCOpenid(), token, 3, TimeUnit.DAYS);
@@ -180,22 +153,20 @@ public class MaTechnicianController extends BaseController {
         // 返回用户信息
         // 记录登录信息
         AsyncManager.me().execute(AsyncFactory.recordLogininfor(wxUser.getCOpenid(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
-        result.setResult(wxUser);
         // 登录成功删除验证码
         redisTemplate.delete(PHONE_THREEUSERPARTCLIENT_CODE_KEY + thirdPartyLoginsVo.getPhone());
-        result.success("登录成功");
-        return result;
+        return R.ok(wxUser, "登录成功");
     }
 
     /**
      * 商户忘记密码接口
      *
      * @param thirdPartyLoginsVo
-     * @return Result<?>
+     * @return R<?>
      */
     @PostMapping("/resetPassword")
     @ApiOperation("商户忘记密码接口")
-    public Result<?> resetPassword(@RequestBody ThirdPartyLoginsVo thirdPartyLoginsVo) {
+    public R<?> resetPassword(@RequestBody ThirdPartyLoginsVo thirdPartyLoginsVo) {
         // 核心正则表达式:
         // ^ 表示开头,$ 表示结尾
         // [a-zA-Z0-9] 表示只能是字母或数字
@@ -205,19 +176,19 @@ public class MaTechnicianController extends BaseController {
             boolean isMatch = Pattern.matches(regex, thirdPartyLoginsVo.getPassWord().trim());
             if (!isMatch) {
                 // 根据需求返回指定的异常提示
-                return Result.error("请输入8-20位数字/字母组合");
+                return R.fail("请输入8-20位数字/字母组合");
             }
             // 短信验证 sys:clientLogin:phone:手机号
             String msg = redisTemplate.opsForValue().get(PHONE_THREEUSERPARTCLIENT_CODE_KEY + thirdPartyLoginsVo.getPhone());
             log.info("短信验证码的值:{}", msg);
             if (StringUtils.isEmpty(msg)) {
-                return Result.error("验证码已失效");
+                return R.fail("验证码已失效");
             }
             if (msg != null && msg.startsWith("\"") && msg.endsWith("\"")) {
                 msg = msg.substring(1, msg.length() - 1);
             }
             if (!thirdPartyLoginsVo.getPhoneMsg().equals(msg)) {
-                return Result.error("短信验证码不正确");
+                return R.fail("短信验证码不正确");
             }
             // 重置密码逻辑
             LambdaUpdateWrapper<MaTechnician> updateWrapper = new LambdaUpdateWrapper<>();
@@ -228,7 +199,7 @@ public class MaTechnicianController extends BaseController {
             maTechnicianService.update(updateWrapper);
             // 重置密码成功删除验证码
             redisTemplate.delete(PHONE_THREEUSERPARTCLIENT_CODE_KEY + thirdPartyLoginsVo.getPhone());
-            return Result.ok("重置密码成功");
+            return R.ok("重置密码成功");
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -239,15 +210,15 @@ public class MaTechnicianController extends BaseController {
      * 校验图形验证码接口
      *
      * @param req
-     * @return Result<?>
+     * @return R<?>
      */
     @PostMapping("/getValidateCaptcha")
     @ApiOperation("校验图形验证码接口")
-    public Result<?> getValidateCaptcha(@RequestBody ValidateCaptchaDto req) {
+    public R<?> getValidateCaptcha(@RequestBody ValidateCaptchaDto req) {
         try {
             log.info("校验图形验证码的请求参数:{}", JSON.toJSONString(req));
             validateCaptcha(req.getPhone(), req.getCode(), req.getUuid());
-            return Result.ok("校验成功");
+            return R.ok("校验成功");
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -258,38 +229,38 @@ public class MaTechnicianController extends BaseController {
      * 商户入驻申请接口
      *
      * @param req
-     * @return Result<?>
+     * @return R<?>
      */
     @PostMapping("/apply")
     @ApiOperation("商户入驻申请接口")
-    public Result<?> apply(@RequestBody MaTechnicianAppAddVo req) {
+    public R<?> apply(@RequestBody MaTechnicianAppAddVo req) {
         try {
             // 1. 基础参数校验
             if (StringUtils.isAnyBlank(req.getTeName(), req.getTePhone(),  req.getAvatar())) {
-                return Result.error("必填项不能为空");
+                return R.fail("必填项不能为空");
             }
             //校验性别不能为空
             if (Objects.isNull(req.getTeSex())) {
-                return Result.error("性别不能为空");
+                return R.fail("性别不能为空");
             }
             // 短信验证
             String msg = redisTemplate.opsForValue().get(PHONE_THREEUSERPARTCLIENT_CODE_KEY + req.getTePhone());
             log.info("短信验证码的值:{}", msg);
             /*if (StringUtils.isEmpty(msg)) {
-                return Result.error("验证码已失效");
+                return R.fail("验证码已失效");
             }
             if (msg != null && msg.startsWith("\"") && msg.endsWith("\"")) {
                 msg = msg.substring(1, msg.length() - 1);
             }
             if (!req.getPhoneMsg().equals(msg)) {
-                return Result.error("短信验证码不正确");
+                return R.fail("短信验证码不正确");
             }*/
             if (StringUtils.isNotEmpty(req.getPhoneImgMsg())) {
                 validateCaptcha(req.getTeName(), req.getPhoneImgMsg(), req.getUuid());
             }
             // 2. 调用业务层处理入驻申请
             maTechnicianService.apply(req);
-            return Result.ok("提交成功,进入审核流程");
+            return R.ok("提交成功,进入审核流程");
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -324,14 +295,14 @@ public class MaTechnicianController extends BaseController {
      * 申请入驻
      *
      * @param req
-     * @return Result<?>
+     * @return R<?>
      */
     @PostMapping("/applyFile")
     @ApiOperation("申请入驻")
-    public Result applyFile(@RequestBody MerchantApplyFileRequestDto req) {
+    public R<?> applyFile(@RequestBody MerchantApplyFileRequestDto req) {
         try {
             maTechnicianService.applyFile(req);
-            return Result.ok("申请入驻成功");
+            return R.ok("申请入驻成功");
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -343,9 +314,9 @@ public class MaTechnicianController extends BaseController {
      */
     @PostMapping("/wait/list")
     @ApiOperation("获取待接单列表")
-    public Result<List<WaitOrderDTO>> getWaitOrder(@RequestBody WaitOrderQueryDTO query) {
+    public R<List<WaitOrderDTO>> getWaitOrder(@RequestBody WaitOrderQueryDTO query) {
         List<WaitOrderDTO> list = maTechnicianService.listWaitOrder(query);
-        return Result.ok(list);
+        return R.ok(list);
     }
 
     /**
@@ -354,9 +325,9 @@ public class MaTechnicianController extends BaseController {
      */
     @PostMapping("/accept")
     @ApiOperation("滑动接单")
-    public Result<String> acceptOrder(@RequestBody AcceptOrderReqDTO req) {
+    public R<String> acceptOrder(@RequestBody AcceptOrderReqDTO req) {
         String tip = maTechnicianService.acceptOrder(req);
-        return Result.ok(tip);
+        return R.ok(tip);
     }
 
     /**
@@ -365,9 +336,9 @@ public class MaTechnicianController extends BaseController {
      */
     @GetMapping("/rest/confirm")
     @ApiOperation("确认接单")
-    public Result<String> confirmRestAccept(@RequestParam Long techId, @RequestParam Long orderId) {
+    public R<String> confirmRestAccept(@RequestParam Long techId, @RequestParam Long orderId) {
         String tip = maTechnicianService.confirmRestAccept(techId, orderId);
-        return Result.ok(tip);
+        return R.ok(tip);
     }
 
     /**
@@ -376,9 +347,9 @@ public class MaTechnicianController extends BaseController {
      */
     @PostMapping("/refuse")
     @ApiOperation("拒绝接单")
-    public Result<Void> refuseOrder(@RequestBody RefuseOrderReqDTO req) {
+    public R<String> refuseOrder(@RequestBody RefuseOrderReqDTO req) {
         maTechnicianService.refuseOrder(req);
-        return Result.ok("拒绝成功");
+        return R.ok("拒绝成功");
     }
 
     /**
@@ -386,13 +357,13 @@ public class MaTechnicianController extends BaseController {
      *
      * @param userId 商户ID
      * @param forceConfirm 是否强制切换 true:强制切换 false:不强制切换
-     * @return Result<?>
+     * @return R<?>
      */
     @GetMapping("/switchToOffline")
     @ApiOperation("商户状态切换")
-    public Result switchToOffline(@RequestParam Long userId, @RequestParam Boolean forceConfirm) {
+    public R<?> switchToOffline(@RequestParam Long userId, @RequestParam Boolean forceConfirm) {
         try {
-            return maTechnicianService.switchToOffline(userId, forceConfirm);
+            return toR(maTechnicianService.switchToOffline(userId, forceConfirm));
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -403,17 +374,17 @@ public class MaTechnicianController extends BaseController {
      * 查询商户信息
      *
      * @param openid 微信openid
-     * @return Result<?>
+     * @return R<?>
      */
     @GetMapping("/getTechnician")
     @ApiOperation("查询商户信息")
-    public Result<?> getTechnician(@RequestParam String openid) {
+    public R<?> getTechnician(@RequestParam String openid) {
         try {
             MerchantAuditFile technicianInfo = maTechnicianService.getTechnicianInfo(openid);
-            return Result.ok(technicianInfo.getMerchant());
+            return R.ok(technicianInfo.getMerchant());
         } catch (Exception e) {
             log.error("查询商户信息失败", e);
-            return Result.error(e.getMessage());
+            return R.fail(e.getMessage());
         }
     }
 
@@ -421,16 +392,16 @@ public class MaTechnicianController extends BaseController {
      * 查询商户信息和入驻资料
      *
      * @param openid 微信openid
-     * @return Result<?>
+     * @return R<?>
      */
     @GetMapping("/getTechnicianInfo")
     @ApiOperation("查询商户信息和入驻资料")
-    public Result<?> getTechnicianInfo(@RequestParam("openid") String openid) {
+    public R<?> getTechnicianInfo(@RequestParam("openid") String openid) {
         try {
-            return Result.ok(maTechnicianService.getTechnicianInfo(openid));
+            return R.ok(maTechnicianService.getTechnicianInfo(openid));
         } catch (Exception e) {
             log.error("查询商户信息和入驻资料失败", e);
-            return Result.error(e.getMessage());
+            return R.fail(e.getMessage());
         }
     }
 
@@ -438,17 +409,17 @@ public class MaTechnicianController extends BaseController {
      * 修改商户信息
      *
      * @param req
-     * @return Result<?>
+     * @return R<?>
      */
     @PostMapping("/updateTechnician")
-    @ApiOperation("修改商户信息接口")
-    public Result<?> updateTechnician(@RequestBody MerchantApplyFileRequestDto req) {
+    @ApiOperation("修改商户信息")
+    public R<?> updateTechnician(@Valid @RequestBody MerchantApplyFileRequestDto req) {
         try {
             maTechnicianService.updateTechnician(req);
-            return Result.ok("修改成功");
+            return R.ok("修改成功");
         } catch (Exception e) {
             log.error("修改商户信息失败", e);
-            return Result.error(e.getMessage());
+            return R.fail(e.getMessage());
         }
     }
 
@@ -457,10 +428,10 @@ public class MaTechnicianController extends BaseController {
      */
     @PreAuthorize("@ss.hasPermi('technician:technician:list')")
     @GetMapping("/list")
-    public TableDataInfo list(MaTechnician maTechnician) {
+    public R<TableDataInfo> list(MaTechnician maTechnician) {
         startPage();
         List<MaTechnician> list = maTechnicianService.selectMaTechnicianList(maTechnician);
-        return getDataTable(list);
+        return R.ok(getDataTable(list));
     }
 
     /**
@@ -480,8 +451,8 @@ public class MaTechnicianController extends BaseController {
      */
     @PreAuthorize("@ss.hasPermi('technician:technician:query')")
     @GetMapping(value = "/{id}")
-    public AjaxResult getInfo(@PathVariable("id") Long id) {
-        return success(maTechnicianService.selectMaTechnicianById(id));
+    public R<MaTechnician> getInfo(@PathVariable("id") Long id) {
+        return R.ok(maTechnicianService.selectMaTechnicianById(id));
     }
 
     /**
@@ -491,24 +462,24 @@ public class MaTechnicianController extends BaseController {
     @PreAuthorize("@ss.hasPermi('technician:technician:add')")
     @Log(title = "技师", businessType = BusinessType.INSERT)
     @PostMapping
-    public AjaxResult add(@RequestBody MaTechnicianAppAddVo maTechnicianAppAddVo) {
-        return toAjax(maTechnicianService.insertMaTechnician(maTechnicianAppAddVo));
+    public R<Void> add(@RequestBody MaTechnicianAppAddVo maTechnicianAppAddVo) {
+        return toR(maTechnicianService.insertMaTechnician(maTechnicianAppAddVo));
     }
 
     /**
      * 后台新增商户
      *
      * @param dto 商户新增DTO
-     * @return AjaxResult 结果
+     * @return R<Void> 结果
      */
     @ApiOperation("后台新增商户")
     @PreAuthorize("@ss.hasPermi('technician:technician:add')")
     @Log(title = "商户", businessType = BusinessType.INSERT)
     @PostMapping("/merchant")
-    public AjaxResult addMerchant(@RequestBody MaTechnicianMerchantAddDTO dto) {
+    public R<Void> addMerchant(@RequestBody MaTechnicianMerchantAddDTO dto) {
         try {
             LoginUser loginUser = getLoginUser();
-            return toAjax(maTechnicianService.insertMerchant(dto, loginUser));
+            return toR(maTechnicianService.insertMerchant(dto, loginUser));
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -520,16 +491,16 @@ public class MaTechnicianController extends BaseController {
      *
      * @param id  商户ID
      * @param dto 商户编辑DTO
-     * @return AjaxResult 结果
+     * @return R<Void> 结果
      */
     @ApiOperation("后台编辑商户")
     @PreAuthorize("@ss.hasPermi('technician:technician:edit')")
     @Log(title = "商户", businessType = BusinessType.UPDATE)
     @PutMapping("/merchant/{id}")
-    public AjaxResult editMerchant(@PathVariable("id") Integer id, @RequestBody MaTechnicianMerchantAddDTO dto) {
+    public R<Void> editMerchant(@PathVariable("id") Integer id, @RequestBody MaTechnicianMerchantAddDTO dto) {
         try {
             LoginUser loginUser = getLoginUser();
-            return toAjax(maTechnicianService.updateMerchant(id, dto, loginUser));
+            return toR(maTechnicianService.updateMerchant(id, dto, loginUser));
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -547,7 +518,7 @@ public class MaTechnicianController extends BaseController {
     @PreAuthorize("@ss.hasPermi('technician:technician:edit')")
     @Log(title = "商户合同", businessType = BusinessType.UPDATE)
     @PostMapping("/merchant/{id}/contract")
-    public R uploadMerchantContract(@PathVariable("id") Integer id, @RequestBody Map<String, Object> map) {
+    public R<Integer> uploadMerchantContract(@PathVariable("id") Integer id, @RequestBody Map<String, Object> map) {
         try {
             LoginUser loginUser = getLoginUser();
             return R.ok(maTechnicianService.uploadMerchantContract(id, map, loginUser));
@@ -567,8 +538,7 @@ public class MaTechnicianController extends BaseController {
     @ApiOperation("后台查询商户入驻审核列表")
     @PreAuthorize("@ss.hasPermi('technician:technician:list')")
     @GetMapping("/merchant/audit/list")
-    public R<Page<MaTechnicianAuditListVO>> merchantAuditList
-    (Page<MaTechnicianAuditListVO> page, MaTechnicianAuditQueryDTO dto) {
+    public R<Page<MaTechnicianAuditListVO>> merchantAuditList(Page<MaTechnicianAuditListVO> page, MaTechnicianAuditQueryDTO dto) {
         try {
             return R.ok(maTechnicianService.selectMerchantAuditList(page, dto));
         } catch (Exception e) {
@@ -578,21 +548,42 @@ public class MaTechnicianController extends BaseController {
     }
 
     /**
-     * 商户入驻审核
+     * 商户入驻审核
      *
      * @param id  商户ID
      * @param dto 审核提交参数
-     * @return AjaxResult 结果
+     * @return R<Void> 结果
      */
     @ApiOperation("商户入驻审核")
     @PreAuthorize("@ss.hasPermi('technician:technician:edit')")
     @Log(title = "商户入驻审核", businessType = BusinessType.UPDATE)
     @PutMapping("/merchant/audit/{id}/submit")
-    public AjaxResult submitMerchantAudit(@PathVariable("id") Integer id, @RequestBody MaTechnicianAuditSubmitDTO
-                                                                                  dto) {
+    public R<Void> submitMerchantAudit(@PathVariable("id") Integer id, @RequestBody MaTechnicianAuditSubmitDTO dto) {
         try {
             LoginUser loginUser = getLoginUser();
-            return toAjax(maTechnicianService.submitMerchantAudit(id, dto, loginUser));
+            return toR(maTechnicianService.submitMerchantAudit(id, dto, loginUser));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    /**
+     * 商户申请入驻审核
+     *
+     * @param id  商户ID
+     * @param dto 审核提交参数
+     * @return R<Void> 结果
+     */
+    @ApiOperation("商户申请入驻审核")
+    @PreAuthorize("@ss.hasPermi('technician:technician:edit')")
+    @Log(title = "商户申请入驻审核", businessType = BusinessType.UPDATE)
+    @PutMapping("/merchant/audit/{id}/applySubmit")
+    public R<Void> applyMerchantAudit(@PathVariable("id") Integer id, @RequestBody MaTechnicianAuditSubmitDTO dto) {
+        try {
+            LoginUser loginUser = getLoginUser();
+            return toR(maTechnicianService.applyMerchantAudit(id, dto, loginUser));
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -604,18 +595,16 @@ public class MaTechnicianController extends BaseController {
      *
      * @param id  商户ID
      * @param dto 待审核通过参数
-     * @return AjaxResult 结果
+     * @return R<Void> 结果
      */
     @ApiOperation("待审核页面审核通过商户")
     @PreAuthorize("@ss.hasPermi('technician:technician:edit')")
     @Log(title = "商户待审核通过", businessType = BusinessType.UPDATE)
     @PutMapping("/merchant/audit/{id}/approve")
-    public AjaxResult approvePendingMerchantAudit(@PathVariable("id") Integer id,
-                                                  @RequestBody MaTechnicianPendingAuditSubmitDTO
-                                                          dto) {
+    public R<Void> approvePendingMerchantAudit(@PathVariable("id") Integer id, @RequestBody MaTechnicianPendingAuditSubmitDTO dto) {
         try {
             LoginUser loginUser = getLoginUser();
-            return toAjax(maTechnicianService.approvePendingMerchantAudit(id, dto, loginUser));
+            return toR(maTechnicianService.approvePendingMerchantAudit(id, dto, loginUser));
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException(e);
@@ -684,8 +673,8 @@ public class MaTechnicianController extends BaseController {
     @PreAuthorize("@ss.hasPermi('technician:technician:edit')")
     @Log(title = "技师", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody MaTechnicianAppAddVo maTechnicianAppAddVo) {
-        return toAjax(maTechnicianService.updateMaTechnician(maTechnicianAppAddVo));
+    public R<Void> edit(@RequestBody MaTechnicianAppAddVo maTechnicianAppAddVo) {
+        return toR(maTechnicianService.updateMaTechnician(maTechnicianAppAddVo));
     }
 
     /**
@@ -694,8 +683,8 @@ public class MaTechnicianController extends BaseController {
     @PreAuthorize("@ss.hasPermi('technician:technician:remove')")
     @Log(title = "技师", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
-    public AjaxResult remove(@PathVariable Long[] ids) {
-        return toAjax(maTechnicianService.deleteMaTechnicianByIds(ids));
+    public R<Void> remove(@PathVariable Long[] ids) {
+        return toR(maTechnicianService.deleteMaTechnicianByIds(ids));
     }
 
     /**
@@ -703,9 +692,9 @@ public class MaTechnicianController extends BaseController {
      */
     @GetMapping("/getServiceCategoryList")
     @ApiOperation("获取服务类目列表")
-    public AjaxResult getServiceCategoryList() {
+    public R<List<ServiceCategory>> getServiceCategoryList() {
         List<ServiceCategory> list = serviceCategoryService.listH5ServiceCategory();
-        return AjaxResult.success(list);
+        return R.ok(list);
     }
 
     /**
@@ -714,10 +703,9 @@ public class MaTechnicianController extends BaseController {
      */
     @PostMapping("/getSkillList")
     @ApiOperation("获取技能列表")
-    public Result<List<MaProject>> getSkillList(@RequestBody MaProjectGetVo req) {
-
+    public R<List<MaProject>> getSkillList(@RequestBody MaProjectGetVo req) {
         List<MaProject> list = maTechnicianService.selectMaTechnicianListBy(req.getUserId(), req.getAuditStatus());
-        return Result.ok(list);
+        return R.ok(list);
     }
 
     /**
@@ -728,10 +716,8 @@ public class MaTechnicianController extends BaseController {
      */
     @PostMapping("/getNotApplyList")
     @ApiOperation("查询未开通的服务项目列表")
-    public Result<?> getNotApplyList(@RequestBody MaProjectGetVo req) {
-
-        return Result.ok(maTechnicianService.getNotApplyList(req.getUserId(), req.getTypeId()));
-
+    public R<?> getNotApplyList(@RequestBody MaProjectGetVo req) {
+        return R.ok(maTechnicianService.getNotApplyList(req.getUserId(), req.getTypeId()));
     }
 
     /**
@@ -739,9 +725,8 @@ public class MaTechnicianController extends BaseController {
      */
     @PostMapping("/applyForService")
     @ApiOperation("申请开通新服务")
-    public AjaxResult applyForService(@RequestBody MaProjectSaveDto dto) {
-
-        return toAjax(maTechnicianService.applyForService(dto));
+    public R<Void> applyForService(@RequestBody MaProjectSaveDto dto) {
+        return toR(maTechnicianService.applyForService(dto));
     }
 
     /**
@@ -752,7 +737,7 @@ public class MaTechnicianController extends BaseController {
      */
     @PostMapping("/updateApply")
     @ApiOperation("重新申请开通新服务")
-    public Result<?> updateApply(@RequestBody MaProjectUpdateDto req) {
+    public R<?> updateApply(@RequestBody MaProjectUpdateDto req) {
         if (StringUtils.isNotEmpty(req.getProjectId()) && StringUtils.isNotEmpty(req.getApplyReason())) {
             LambdaUpdateWrapper<MaProject> updateWrapper = new LambdaUpdateWrapper<>();
             updateWrapper.eq(MaProject::getId, req.getProjectId());
@@ -760,7 +745,7 @@ public class MaTechnicianController extends BaseController {
             updateWrapper.set(MaProject::getAuditStatus, 0);
             maProjectService.update(updateWrapper);
         }
-        return Result.ok("重新申请成功,提交到审核阶段");
+        return R.ok("重新申请成功,提交到审核阶段");
 
     }
 
@@ -772,7 +757,7 @@ public class MaTechnicianController extends BaseController {
      */
     @PostMapping("/updateMaProject")
     @ApiOperation("申请下架,删除服务项目,编辑售价价格")
-    public Result<?> updateMaProject(@RequestBody MaProjectUpdateDto req) {
+    public R<?> updateMaProject(@RequestBody MaProjectUpdateDto req) {
         String message = "";
         if (StringUtils.isNotEmpty(req.getProjectId())) {
             if (req.getIsDelete()) {
@@ -799,19 +784,19 @@ public class MaTechnicianController extends BaseController {
 
             }
         }
-        return Result.ok(message);
+        return R.ok(message);
     }
 
     /**
      * 商户入驻信息
      *
      * @param userId
-     * @return Result<?>
+     * @return R<?>
      */
     @GetMapping("/getTechnicianList")
     @ApiOperation("商户入驻信息")
-    public Result<?> getTechnicianList(@RequestParam(value = "userId") Integer userId) {
-        return Result.ok(maTechnicianService.getTechnicianList(userId));
+    public R<?> getTechnicianList(@RequestParam(value = "userId") Integer userId) {
+        return R.ok(maTechnicianService.getTechnicianList(userId));
     }
 
     /**
@@ -823,8 +808,19 @@ public class MaTechnicianController extends BaseController {
 
     @GetMapping("/getContractRecords")
     @ApiOperation("查询商户合同记录信息")
-    public Result<?> getContractRecords(@RequestParam(value = "userId") Long userId) {
-        return Result.ok(maTechnicianService.getContractRecords(userId));
+    public R<?> getContractRecords(@RequestParam(value = "userId") Long userId) {
+        return R.ok(maTechnicianService.getContractRecords(userId));
+    }
+
+    private R<Void> toR(int rows) {
+        return rows > 0 ? R.ok() : R.fail();
+    }
+
+    private R<?> toR(Result<?> result) {
+        if (result == null) {
+            return R.fail();
+        }
+        return result.isSuccess() ? R.ok(result.getResult(), result.getMessage()) : R.fail(result.getResult(), result.getMessage());
     }
 
 }

+ 46 - 2
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TechnicianMomentController.java

@@ -8,8 +8,10 @@ import com.ylx.common.core.domain.model.WxLoginUser;
 import com.ylx.common.enums.BusinessType;
 import com.ylx.massage.domain.dto.MomentAuditDTO;
 import com.ylx.massage.domain.dto.MomentManageQueryDTO;
+import com.ylx.massage.domain.dto.MomentRecommendDTO;
 import com.ylx.massage.domain.dto.PublishMomentDTO;
 import com.ylx.massage.domain.dto.EditMomentDTO;
+import com.ylx.massage.domain.dto.MomentListStatusDTO;
 import com.ylx.massage.domain.vo.MomentDetailVO;
 import com.ylx.massage.domain.vo.MomentListVO;
 import com.ylx.massage.domain.vo.MomentManageVO;
@@ -226,7 +228,7 @@ public class TechnicianMomentController extends BaseController {
      * 编辑动态
      *
      * @param dto 编辑动态请求对象
-     * @return R 动态ID
+     * @return R<Long> 动态ID
      */
     @PostMapping("/edit")
     @ApiOperation("编辑动态")
@@ -343,7 +345,7 @@ public class TechnicianMomentController extends BaseController {
     @ApiOperation("审核动态")
     public R<Boolean> auditMoment(@Valid @RequestBody MomentAuditDTO dto) {
         try {
-            log.info("审核动态,动态ID:{},审核状态:{},拒绝原因:{}", dto.getMomentId(), dto.getAuditStatus(), dto.getRejectReason());
+            log.info("请求的审核动态参数:{}", JSON.toJSONString(dto));
             Boolean result = momentService.auditMoment(dto);
             String message = dto.getAuditStatus() == 2 ? "审核通过" : "审核拒绝";
             return R.ok(result, message);
@@ -353,6 +355,48 @@ public class TechnicianMomentController extends BaseController {
         }
     }
 
+    /**
+     * 更新动态推荐状态
+     *
+     * @param momentId 动态ID
+     * @param dto      推荐状态请求对象
+     * @return R<Boolean> 是否成功
+     */
+    @PutMapping("/manage/{momentId}/recommend")
+    @ApiOperation("更新动态推荐状态")
+    public R<Boolean> updateRecommendStatus(@ApiParam("动态ID") @PathVariable Long momentId, @Valid @RequestBody MomentRecommendDTO dto) {
+        try {
+            log.info("更新动态推荐状态,动态ID:{},是否推荐:{}", momentId, dto.getIsRecommend());
+            Boolean result = momentService.updateRecommendStatus(momentId, dto.getIsRecommend());
+            String message = dto.getIsRecommend() == 1 ? "推荐成功" : "取消推荐成功";
+            return R.ok(result, message);
+        } catch (Exception e) {
+            log.error("更新动态推荐状态失败", e);
+            return R.fail("更新动态推荐状态失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 更新动态上架状态
+     *
+     * @param momentId 动态ID
+     * @param dto      上架状态请求对象
+     * @return R<Boolean> 是否成功
+     */
+    @PutMapping("/manage/{momentId}/list-status")
+    @ApiOperation("更新动态上架状态")
+    public R<Boolean> updateListStatus(@ApiParam("动态ID") @PathVariable Long momentId, @Valid @RequestBody MomentListStatusDTO dto) {
+        try {
+            log.info("更新动态上架状态,动态ID:{},是否上架:{}", momentId, dto.getIsList());
+            Boolean result = momentService.updateListStatus(momentId, dto.getIsList());
+            String message = dto.getIsList() == 1 ? "上架成功" : "下架成功";
+            return R.ok(result, message);
+        } catch (Exception e) {
+            log.error("更新动态上架状态失败", e);
+            return R.fail("更新动态上架状态失败:" + e.getMessage());
+        }
+    }
+
     /**
      * 根据动态ID查询动态详情
      *

+ 1 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/MaTechnician.java

@@ -42,6 +42,7 @@ public class MaTechnician extends BaseEntity {
     @Excel(name = "昵称")
     @TableField("te_nick_name")
     private String teNickName;
+
     /**
      * 微信小程序登录会话密钥
      */

+ 23 - 17
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TechnicianMoment.java

@@ -1,8 +1,11 @@
 package com.ylx.massage.domain;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
+
+import java.io.Serializable;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
@@ -11,7 +14,9 @@ import java.time.LocalDateTime;
  */
 @Data
 @TableName("t_technician_moment")
-public class TechnicianMoment {
+public class TechnicianMoment implements Serializable {
+
+    private static final long serialVersionUID = 1L;
 
     /**
      * 主键ID
@@ -54,11 +59,6 @@ public class TechnicianMoment {
      */
     private LocalDateTime publishTime;
 
-    /**
-     * 状态:1-正常,2-删除
-     */
-    private Integer status;
-
     /**
      * 城市编码
      */
@@ -85,19 +85,14 @@ public class TechnicianMoment {
     private BigDecimal latitude;
 
     /**
-     * 距离(单位:米)
+     * 是否推荐:0-否,1-是
      */
-    //private Double distance;
+    private Integer isRecommend;
 
     /**
-     * 创建时间
-     */
-    private LocalDateTime createTime;
-
-    /**
-     * 更新时间
+     * 是否上架:0-否,1-是
      */
-    private LocalDateTime updateTime;
+    private Integer isList;
 
     /**
      * 审核状态:0-草稿,1-待审核,2-审核通过,3-审核拒绝
@@ -110,7 +105,18 @@ public class TechnicianMoment {
     private String rejectReason;
 
     /**
-     * 可见范围:1-公开
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+
+    /**
+     * 是否删除(0否1是)
      */
-    private Integer visibleRange;
+    @TableLogic
+    private Integer isDelete;
 }

+ 0 - 7
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/EditMomentDTO.java

@@ -65,13 +65,6 @@ public class EditMomentDTO {
     @ApiModelProperty("视频封面URL")
     private String videoCoverUrl;
 
-    /**
-     * 可见范围:1-公开
-     */
-    @ApiModelProperty(value = "可见范围:1-公开", required = true)
-    @NotNull(message = "可见范围不能为空")
-    private Integer visibleRange;
-
     /**
      * 纬度
      */

+ 2 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MerchantApplyFileDto.java

@@ -2,6 +2,7 @@ package com.ylx.massage.domain.dto;
 
 import lombok.Data;
 
+import javax.validation.Valid;
 import java.math.BigDecimal;
 import java.util.List;
 
@@ -37,5 +38,6 @@ public class MerchantApplyFileDto {
     /**
      * 同一文件类型下的多个文件。
      */
+    @Valid
     private List<MerchantApplyFileDto> files;
 }

+ 4 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MerchantApplyFileRequestDto.java

@@ -3,6 +3,8 @@ package com.ylx.massage.domain.dto;
 import com.ylx.massage.domain.MaTechnician;
 import lombok.Data;
 
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
 import java.util.List;
 
 @Data
@@ -10,10 +12,12 @@ public class MerchantApplyFileRequestDto {
     /**
      * 商户信息
      */
+    @NotNull(message = "商户信息不能为空")
     private MaTechnician technician;
 
     /**
      * 申请入驻文件
      */
+    @Valid
     private  List<MerchantApplyFileDto> req;
 }

+ 0 - 1
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MomentAuditDTO.java

@@ -21,7 +21,6 @@ public class MomentAuditDTO {
       * 拒绝原因(拒绝时必填)
       */
     private String rejectReason;
-
 }
 
 

+ 22 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MomentListStatusDTO.java

@@ -0,0 +1,22 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 动态上架状态DTO
+ */
+@Data
+@ApiModel("动态上架状态参数")
+public class MomentListStatusDTO {
+
+    /**
+     * 是否上架:0-否,1-是
+     */
+    @NotNull(message = "上架状态不能为空")
+    @ApiModelProperty("是否上架:0-否,1-是")
+    private Integer isList;
+}

+ 22 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MomentRecommendDTO.java

@@ -0,0 +1,22 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 动态推荐状态DTO
+ */
+@Data
+@ApiModel("动态推荐状态参数")
+public class MomentRecommendDTO {
+
+    /**
+     * 是否推荐:0-否,1-是
+     */
+    @NotNull(message = "是否推荐不能为空")
+    @ApiModelProperty("是否推荐:0-否,1-是")
+    private Integer isRecommend;
+}

+ 50 - 4
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MaTechnicianCertificateVO.java

@@ -4,6 +4,8 @@ import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.util.List;
+
 /**
  * 后台商户证照展示对象
  */
@@ -11,37 +13,81 @@ import lombok.Data;
 @ApiModel(value = "MaTechnicianCertificateVO", description = "后台商户证照展示对象")
 public class MaTechnicianCertificateVO {
 
+    /**
+     * 商户ID
+     */
     @ApiModelProperty("商户ID")
     private Integer merchantId;
 
+    /**
+     * 形象照
+     */
     @ApiModelProperty("形象照")
     private String avatar;
 
+    /**
+     * 生活照
+     */
     @ApiModelProperty("生活照")
-    private String lifePhotos;
+    private List<String> lifePhotos;
+
+    /**
+     * 宣传视频
+     */
+    @ApiModelProperty("宣传视频")
+    private String promotionVideo;
 
+    /**
+     * 身份证人像面
+     */
     @ApiModelProperty("身份证人像面")
     private String idCardFrout;
+
+    /**
+     * 身份证国徽面
+     */
     @ApiModelProperty("身份证国徽面")
     private String idCardBack;
+
+    /**
+     * 手持身份证照片
+     */
     @ApiModelProperty("手持身份证照片")
     private String idCardHandheld;
 
+    /**
+     * 健康证
+     */
     @ApiModelProperty("健康证")
     private String healthCertificate;
 
+    /**
+     * 从业资格证
+     */
     @ApiModelProperty("从业资格证")
     private String qualificationCertificate;
 
+    /**
+     * 无犯罪证明
+     */
     @ApiModelProperty("无犯罪证明")
     private String noCrimeRecord;
 
+    /**
+     * 承诺书
+     */
     @ApiModelProperty("承诺书")
     private String commitmentPdf;
 
-    @ApiModelProperty("承诺视频")
-    private String commitmentVideo;
-
+    /**
+     * 承诺录音
+     */
     @ApiModelProperty("承诺录音")
     private String commitmentAudio;
+
+    /**
+     * 承诺视频
+     */
+    @ApiModelProperty("承诺视频")
+    private String commitmentVideo;
 }

+ 12 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MomentManageVO.java

@@ -66,4 +66,16 @@ public class MomentManageVO {
      */
     @ApiModelProperty("审核状态:0-草稿,1-待审核,2-审核通过,3-审核拒绝")
     private Integer auditStatus;
+
+    /**
+     * 是否推荐:0-否,1-是
+     */
+    @ApiModelProperty("是否推荐:0-否,1-是")
+    private Integer isRecommend;
+
+    /**
+     * 是否上架:0-否,1-是
+     */
+    @ApiModelProperty("是否上架:0-否,1-是")
+    private Integer isList;
 }

+ 27 - 22
nightFragrance-massage/src/main/java/com/ylx/massage/enums/FileTypeEnum.java

@@ -22,54 +22,59 @@ public enum FileTypeEnum {
     LIFE_PHOTO("2", "生活照"),
 
     /**
-     * 3-身份证正面
+     * 3-宣传视频
      */
-    ID_CARD_FRONT("3", "身份证正面"),
+    PROMOTION_VIDEO("3", "宣传视频"),
 
     /**
-     * 4-身份证
+     * 4-身份证
      */
-    ID_CARD_BACK("4", "身份证反面"),
+    ID_CARD_FRONT("4", "身份证正面"),
 
     /**
-     * 5-手持身份证
+     * 5-身份证反面
      */
-    ID_CARD_HANDHELD("5", "手持身份证"),
+    ID_CARD_BACK("5", "身份证反面"),
 
     /**
-     * 6-健康
+     * 6-手持身份
      */
-    HEALTH_CERT("6", "健康证"),
+    ID_CARD_HANDHELD("6", "手持身份证"),
 
     /**
-     * 7-从业资格
+     * 7-健康
      */
-    QUALIFICATION_CERT("7", "从业资格证"),
+    HEALTH_CERT("7", "健康证"),
 
     /**
-     * 8-无犯罪证明
+     * 8-从业资格证
      */
-    NO_CRIME_RECORD("8", "无犯罪证明"),
+    QUALIFICATION_CERT("8", "从业资格证"),
 
     /**
-     * 9-承诺书
+     * 9-无犯罪证明
      */
-    COMMITMENT_LETTER("9", "承诺书"),
+    NO_CRIME_RECORD("9", "无犯罪证明"),
 
     /**
-     * 10-承诺录音
+     * 10-承诺
      */
-    COMMITMENT_AUDIO("10", "承诺录音"),
+    COMMITMENT_LETTER("10", "承诺书"),
 
     /**
-     * 11-承诺录
+     * 11-承诺录
      */
-    COMMITMENT_VIDEO("11", "承诺录像"),
+    COMMITMENT_AUDIO("11", "承诺录音"),
 
     /**
-     * 12-其他
+     * 12-承诺录像
      */
-    OTHER("12", "其他");
+    COMMITMENT_VIDEO("12", "承诺录像"),
+
+    /**
+     * 13-其他
+     */
+    OTHER("13", "其他");
 
     /**
      * 类型代码 (String)
@@ -85,7 +90,7 @@ public enum FileTypeEnum {
      * 根据 code 获取对应的枚举对象
      *
      * @param code 类型代码
-     * @return 对应的枚举对象,如果未找到则返回 null
+     * @return FileTypeEnum 对应的枚举对象,如果未找到则返回 null
      */
     public static FileTypeEnum getByCode(String code) {
         if (code == null) {
@@ -103,7 +108,7 @@ public enum FileTypeEnum {
      * 根据 code 获取对应的描述信息
      *
      * @param code 类型代码
-     * @return 描述信息,如果未找到则返回空字符串或原值
+     * @return String 描述信息,如果未找到则返回空字符串或原值
      */
     public static String getDescByCode(String code) {
        FileTypeEnum type = getByCode(code);

+ 8 - 6
nightFragrance-massage/src/main/java/com/ylx/massage/mapper/MaTechnicianMapper.java

@@ -32,8 +32,7 @@ import org.apache.ibatis.annotations.Select;
  * @date 2024-03-22
  */
 @Mapper
-public interface MaTechnicianMapper extends BaseMapper<MaTechnician>
-{
+public interface MaTechnicianMapper extends BaseMapper<MaTechnician> {
     /**
      * 查询技师
      *
@@ -48,7 +47,7 @@ public interface MaTechnicianMapper extends BaseMapper<MaTechnician>
      * @param maTechnician 技师
      * @return 技师集合
      */
-     List<MaTechnician> selectMaTechnicianList(MaTechnician maTechnician);
+    List<MaTechnician> selectMaTechnicianList(MaTechnician maTechnician);
 
     /**
      * 新增技师
@@ -81,11 +80,12 @@ public interface MaTechnicianMapper extends BaseMapper<MaTechnician>
      * @return 结果
      */
     public int deleteMaTechnicianByIds(Long[] ids);
+
     /**
      * 后台查询商户列表
      *
      * @param page 分页参数
-     * @param dto 查询条件
+     * @param dto  查询条件
      * @return 商户分页列表
      */
     Page<MaTechnicianMerchantListVO> selectMerchantList(Page<MaTechnicianMerchantListVO> page,
@@ -95,7 +95,7 @@ public interface MaTechnicianMapper extends BaseMapper<MaTechnician>
      * 后台查询商户入驻审核列表
      *
      * @param page 分页参数
-     * @param dto 查询条件
+     * @param dto  查询条件
      * @return 商户入驻审核分页列表
      */
     Page<MaTechnicianAuditListVO> selectMerchantAuditList(Page<MaTechnicianAuditListVO> page,
@@ -183,6 +183,7 @@ public interface MaTechnicianMapper extends BaseMapper<MaTechnician>
 
     /**
      * 查询该城市是否有商户提供服务
+     *
      * @param areaCode
      * @return
      */
@@ -200,10 +201,11 @@ public interface MaTechnicianMapper extends BaseMapper<MaTechnician>
             "      AND p.merchant_type = 0" +
             "      AND p.project_is_enable = 1" +
             ") AS has_merchant_with_service")
-    Boolean isHasMerchantCity(@Param("areaCode")  String areaCode);
+    Boolean isHasMerchantCity(@Param("areaCode") String areaCode);
 
     /**
      * 首页陪玩热门商户推荐销量前五
+     *
      * @param dto
      * @return
      */

+ 10 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/mapper/TechnicianMomentMapper.java

@@ -37,6 +37,16 @@ public interface TechnicianMomentMapper extends BaseMapper<TechnicianMoment> {
      */
     int incrementViewCount(@Param("momentId") Long momentId);
 
+    /**
+     * 更新动态推荐状态
+     */
+    int updateRecommendStatus(@Param("momentId") Long momentId, @Param("isRecommend") Integer isRecommend);
+
+    /**
+     * 更新动态上架状态
+     */
+    int updateListStatus(@Param("momentId") Long momentId, @Param("isList") Integer isList);
+
     /**
      * 根据动态ID查询动态简要详情(包含媒体URL列表)
      *

+ 5 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/IMaTechnicianService.java

@@ -114,6 +114,11 @@ public interface IMaTechnicianService extends IService<MaTechnician> {
      */
     int submitMerchantAudit(Integer id, MaTechnicianAuditSubmitDTO dto, LoginUser loginUser);
 
+    /**
+     * 商户申请入驻提交审核
+     */
+    int applyMerchantAudit(Integer id, MaTechnicianAuditSubmitDTO dto, LoginUser loginUser);
+
     /**
      * 后台待审核页面审核通过商户。
      *

+ 20 - 3
nightFragrance-massage/src/main/java/com/ylx/massage/service/ITechnicianMomentService.java

@@ -1,7 +1,7 @@
 package com.ylx.massage.service;
-
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.ylx.massage.domain.TechnicianMoment;
 import com.ylx.massage.domain.dto.EditMomentDTO;
 import com.ylx.massage.domain.dto.MomentAuditDTO;
 import com.ylx.massage.domain.dto.MomentManageQueryDTO;
@@ -12,14 +12,13 @@ import com.ylx.massage.domain.vo.MomentManageVO;
 import com.ylx.massage.domain.vo.MomentSimpleDetailVO;
 import com.ylx.merchant.domain.dto.MerchantMomentDTO;
 import com.ylx.merchant.domain.vo.MomentVO;
-
 import java.math.BigDecimal;
 import java.util.List;
 
 /**
  * 技师动态服务接口
  */
-public interface ITechnicianMomentService extends IService<com.ylx.massage.domain.TechnicianMoment> {
+public interface ITechnicianMomentService extends IService<TechnicianMoment> {
 
     /**
      * 查询推荐动态列表(不分地区,按日期倒序,同一天按浏览量倒序)
@@ -123,6 +122,24 @@ public interface ITechnicianMomentService extends IService<com.ylx.massage.domai
      */
     Boolean auditMoment(MomentAuditDTO dto);
 
+    /**
+     * 更新动态推荐状态
+     *
+     * @param momentId    动态ID
+     * @param isRecommend 是否推荐:0-否,1-是
+     * @return 是否成功
+     */
+    Boolean updateRecommendStatus(Long momentId, Integer isRecommend);
+
+    /**
+     * 更新动态上架状态
+     *
+     * @param momentId 动态ID
+     * @param isList   是否上架:0-否,1-是
+     * @return 是否成功
+     */
+    Boolean updateListStatus(Long momentId, Integer isList);
+
     /**
      * 根据动态ID查询动态简要详情(包含媒体URL列表)
      *

+ 122 - 65
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/MaTechnicianServiceImpl.java

@@ -207,7 +207,7 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
     }
 
     /**
-     * 商户入驻申请文件上传
+     * 商户申请入驻
      *
      * @param req
      */
@@ -218,7 +218,7 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
             throw new ServiceException("上传参数不能为空");
         }
         Integer merchantId = resolveMerchantId(req.getTechnician());
-        List<MerchantApplyFileDto> files = resolveApplyFiles(merchantId, req.getReq());
+        List<MerchantApplyFileDto> files = resolveApplyFiles(req.getReq());
         if (CollectionUtils.isEmpty(files)) {
             throw new ServiceException("请上传申请入驻文件");
         }
@@ -227,13 +227,12 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
     }
 
     /**
-     * 将新数组格式和旧扁平格式统一转换为单文件记录。
+     * 将文件分组格式转换为单文件记录。
      *
-     * @param merchantId 商户ID
-     * @param reqFiles   入驻资料文件
+     * @param reqFiles 入驻资料文件
      * @return List<MerchantApplyFileDto> 转换后的文件记录
      */
-    private List<MerchantApplyFileDto> resolveApplyFiles(Integer merchantId, List<MerchantApplyFileDto> reqFiles) {
+    private List<MerchantApplyFileDto> resolveApplyFiles(List<MerchantApplyFileDto> reqFiles) {
         if (CollectionUtils.isEmpty(reqFiles)) {
             return Collections.emptyList();
         }
@@ -242,14 +241,9 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
             if (item == null) {
                 throw new ServiceException("入驻资料文件不能为空");
             }
-            if (CollectionUtils.isEmpty(item.getFiles())) {
-                checkApplyFileParam(merchantId, item);
-                files.add(item);
-                continue;
-            }
             checkApplyFileGroupParam(item);
             for (MerchantApplyFileDto child : item.getFiles()) {
-                files.add(buildApplyFileFromGroup(merchantId, item, child));
+                files.add(buildApplyFileFromGroup(item, child));
             }
         }
         return files;
@@ -264,9 +258,12 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
         if (StringUtils.isBlank(group.getFileType())) {
             throw new ServiceException("文件类型不能为空");
         }
+        if (CollectionUtils.isEmpty(group.getFiles())) {
+            throw new ServiceException("文件列表不能为空");
+        }
     }
 
-    private MerchantApplyFileDto buildApplyFileFromGroup(Integer merchantId, MerchantApplyFileDto group, MerchantApplyFileDto child) {
+    private MerchantApplyFileDto buildApplyFileFromGroup(MerchantApplyFileDto group, MerchantApplyFileDto child) {
         if (child == null) {
             throw new ServiceException("入驻资料文件不能为空");
         }
@@ -278,7 +275,7 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
         } else if (!group.getFileType().equals(file.getFileType())) {
             throw new ServiceException("同一组文件类型必须一致");
         }
-        checkApplyFileParam(merchantId, file);
+        checkApplyFileParam(file);
         return file;
     }
 
@@ -310,7 +307,7 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
     /**
      * 修改商户资料。
      * <p>
-     * 基础信息只允许修改昵称和简介;入驻资料文件按商户ID和文件类型进行新增或更新
+     * 基础信息只允许修改昵称和简介;入驻资料文件按本次提交的文件类型整组替换
      * </p>
      *
      * @param req 修改参数
@@ -324,7 +321,9 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
         MaTechnician technician = req.getTechnician();
         Integer merchantId = resolveMerchantId(technician);
         updateTechnicianBaseInfo(technician);
-        upsertApplyFiles(merchantId, req.getReq());
+        // 处理入驻资料文件
+        List<MerchantApplyFileDto> files = resolveApplyFiles(req.getReq());
+        replaceApplyFilesBySubmittedTypes(merchantId, files);
     }
 
     /**
@@ -333,24 +332,20 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
      * @param technician 商户基础信息
      */
     private void updateTechnicianBaseInfo(MaTechnician technician) {
-        if (technician == null) {
-            return;
-        }
         Integer merchantId = technician.getId();
         if (merchantId == null) {
             throw new ServiceException("商户ID不能为空");
         }
-        if (technician.getTeNickName() == null && technician.getTeBrief() == null) {
-            throw new ServiceException("昵称和简介不能为空");
+        if (StringUtils.isBlank(technician.getTeNickName())) {
+            throw new ServiceException("昵称不能为空");
+        }
+        if (StringUtils.isBlank(technician.getTeBrief())) {
+            throw new ServiceException("简介不能为空");
         }
         LambdaUpdateWrapper<MaTechnician> updateWrapper = new LambdaUpdateWrapper<>();
         updateWrapper.eq(MaTechnician::getId, merchantId);
-        if (technician.getTeNickName() != null) {
-            updateWrapper.set(MaTechnician::getTeNickName, technician.getTeNickName());
-        }
-        if (technician.getTeBrief() != null) {
-            updateWrapper.set(MaTechnician::getTeBrief, technician.getTeBrief());
-        }
+        updateWrapper.set(MaTechnician::getTeNickName, technician.getTeNickName().trim());
+        updateWrapper.set(MaTechnician::getTeBrief, technician.getTeBrief().trim());
         // 审核状态默认设为1 待审核
         updateWrapper.set(MaTechnician::getAuditStatus, 1);
         int rows = maTechnicianMapper.update(null, updateWrapper);
@@ -360,35 +355,16 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
     }
 
     /**
-     * 新增或更新商户入驻资料文件。
+     * 按本次提交的文件类型整组替换商户入驻资料文件。
      *
      * @param merchantId 商户ID
      * @param files      入驻资料文件
      */
-    private void upsertApplyFiles(Integer merchantId, List<MerchantApplyFileDto> files) {
+    private void replaceApplyFilesBySubmittedTypes(Integer merchantId, List<MerchantApplyFileDto> files) {
         if (CollectionUtils.isEmpty(files)) {
             return;
         }
-        for (MerchantApplyFileDto file : files) {
-            checkApplyFileParam(merchantId, file);
-            LambdaQueryWrapper<MerchantApplyFile> queryWrapper = new LambdaQueryWrapper<>();
-            queryWrapper.eq(MerchantApplyFile::getMerchantId, merchantId)
-                    .eq(MerchantApplyFile::getFileType, file.getFileType())
-                    .last("LIMIT 1");
-            MerchantApplyFile existsFile = merchantApplyFileMapper.selectOne(queryWrapper);
-            MerchantApplyFile applyFile = new MerchantApplyFile();
-            BeanUtils.copyProperties(file, applyFile);
-            applyFile.setMerchantId(merchantId);
-            applyFile.setUpdateBy(merchantId.toString());
-            applyFile.setIsDelete(NOT_DELETED);
-            if (existsFile == null) {
-                applyFile.setCreateBy(merchantId.toString());
-                merchantApplyFileMapper.insert(applyFile);
-            } else {
-                applyFile.setId(existsFile.getId());
-                merchantApplyFileMapper.updateById(applyFile);
-            }
-        }
+        replaceApplyFiles(merchantId, files);
     }
 
     /**
@@ -404,7 +380,7 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
         throw new ServiceException("商户ID不能为空");
     }
 
-    private void checkApplyFileParam(Integer merchantId, MerchantApplyFileDto file) {
+    private void checkApplyFileParam(MerchantApplyFileDto file) {
         if (file == null) {
             throw new ServiceException("入驻资料文件不能为空");
         }
@@ -667,7 +643,7 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
      * @param id        商户ID
      * @param dto       审核提交参数
      * @param loginUser 当前登录用户
-     * @return 结果
+     * @return int 结果
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -698,8 +674,11 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
 
         MaTechnician maTechnician = new MaTechnician();
         maTechnician.setId(id);
-        // 修改为1:待审核
-        maTechnician.setAuditStatus(1);
+        if (dto.getAuditStatus() == AUDIT_APPROVED) {
+            maTechnician.setAuditStatus(1);
+        } else {
+            maTechnician.setAuditStatus(3);
+        }
         maTechnician.setAuditRemark(auditRemark);
         maTechnician.setApproveTime(DateUtils.getNowDate());
         if (loginUser != null && loginUser.getUser() != null) {
@@ -709,7 +688,59 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
 
         int rows = maTechnicianMapper.submitMerchantAuditById(maTechnician);
         if (rows <= 0) {
-            throw new ServiceException("提交商户审核失败");
+            throw new ServiceException("商户待入驻审核失败");
+        }
+        return rows;
+    }
+
+    /**
+     * 商户申请入驻提交审核
+     *
+     * @param id
+     * @param dto
+     * @param loginUser
+     * @return int
+     */
+    @Override
+    public int applyMerchantAudit(Integer id, MaTechnicianAuditSubmitDTO dto, LoginUser loginUser) {
+        if (id == null) {
+            throw new ServiceException("商户ID不能为空");
+        }
+        if (dto == null) {
+            throw new ServiceException("审核参数不能为空");
+        }
+        checkEnumValue(dto.getAuditStatus(), "审核意见", AUDIT_APPROVED, AUDIT_REJECTED);
+        String auditRemark = dto.getAuditRemark() == null ? "" : dto.getAuditRemark().trim();
+        if (dto.getAuditStatus() == AUDIT_REJECTED && StringUtils.isBlank(auditRemark)) {
+            throw new ServiceException("审核驳回时审核备注不能为空");
+        }
+        if (auditRemark.length() > AUDIT_REMARK_MAX_LENGTH) {
+            throw new ServiceException("审核备注长度不能超过" + AUDIT_REMARK_MAX_LENGTH + "个字符");
+        }
+
+        MaTechnician existsMerchant = maTechnicianMapper.selectMerchantById(id);
+        if (existsMerchant == null || !NOT_DELETED.equals(existsMerchant.getIsDelete())) {
+            throw new ServiceException("商户不存在或已删除");
+        }
+        Integer currentAuditStatus = existsMerchant.getAuditStatus();
+        if (currentAuditStatus == null || (currentAuditStatus != 1)) {
+            throw new ServiceException("当前商户申请入驻已审核,不用重复审核");
+        }
+
+        MaTechnician maTechnician = new MaTechnician();
+        maTechnician.setId(id);
+        //设置审核状态
+        maTechnician.setAuditStatus(dto.getAuditStatus());
+        maTechnician.setAuditRemark(auditRemark);
+        maTechnician.setApproveTime(DateUtils.getNowDate());
+        if (loginUser != null && loginUser.getUser() != null) {
+            maTechnician.setUpdateBy(loginUser.getUser().getUserName());
+        }
+        maTechnician.setUpdateTime(DateUtils.getNowDate());
+
+        int rows = maTechnicianMapper.submitMerchantAuditById(maTechnician);
+        if (rows <= 0) {
+            throw new ServiceException("商户申请入驻审核失败");
         }
         return rows;
     }
@@ -718,7 +749,7 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
      * 后台查询商户证照
      *
      * @param id 商户ID
-     * @return 商户证照
+     * @return MaTechnicianCertificateVO 商户证照
      */
     @Override
     public MaTechnicianCertificateVO selectMerchantCertificate(Integer id) {
@@ -735,7 +766,9 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
         certificate.setMerchantId(merchantApplyFiles.get(0).getMerchantId());
         merchantApplyFiles.forEach(merchant -> {
             certificate.setAvatar(typeFIleUrl(merchant, PORTRAIT.getCode()));
-            certificate.setLifePhotos(typeFIleUrl(merchant, LIFE_PHOTO.getCode()));
+            // 生活照(可能有多张)
+            certificate.setLifePhotos(typeFIleUrl1(merchant, LIFE_PHOTO.getCode()));
+            certificate.setPromotionVideo(typeFIleUrl(merchant, PROMOTION_VIDEO.getCode()));
             certificate.setIdCardFrout(typeFIleUrl(merchant, ID_CARD_FRONT.getCode()));
             certificate.setIdCardBack(typeFIleUrl(merchant, ID_CARD_BACK.getCode()));
             certificate.setIdCardHandheld(typeFIleUrl(merchant, ID_CARD_HANDHELD.getCode()));
@@ -743,24 +776,47 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
             certificate.setQualificationCertificate(typeFIleUrl(merchant, QUALIFICATION_CERT.getCode()));
             certificate.setNoCrimeRecord(typeFIleUrl(merchant, NO_CRIME_RECORD.getCode()));
             certificate.setCommitmentPdf(typeFIleUrl(merchant, COMMITMENT_LETTER.getCode()));
-            certificate.setCommitmentVideo(typeFIleUrl(merchant, COMMITMENT_VIDEO.getCode()));
             certificate.setCommitmentAudio(typeFIleUrl(merchant, COMMITMENT_AUDIO.getCode()));
-
+            certificate.setCommitmentVideo(typeFIleUrl(merchant, COMMITMENT_VIDEO.getCode()));
         });
-
         return certificate;
     }
 
-    private String typeFIleUrl(MerchantApplyFile merchant, String type) {
+    /**
+     * 获取商户证照文件URL
+     *
+     * @param merchant 商户证照
+     * @param type   文件类型
+     * @return 文件URL列表
+     */
+    private String typeFIleUrl(MerchantApplyFile merchantApplyFile, String type) {
+        LambdaQueryWrapper<MerchantApplyFile> queryWrapper1 = Wrappers.lambdaQuery();
+        queryWrapper1.eq(MerchantApplyFile::getMerchantId, merchantApplyFile.getMerchantId());
+        queryWrapper1.eq(MerchantApplyFile::getFileType, type);
+        List<MerchantApplyFile> merchantApplyFiles = merchantApplyFileMapper.selectList(queryWrapper1);
+        if (merchantApplyFiles == null || merchantApplyFiles.isEmpty()) {
+            return null;
+        }
+        return merchantApplyFiles.get(0).getFileUrl();
+    }
+
 
+    /**
+     * 获取商户证照文件URL
+     *
+     * @param merchant 商户证照
+     * @param type   文件类型
+     * @return List<String> 文件URL列表
+     */
+    private List<String> typeFIleUrl1(MerchantApplyFile merchantApplyFile, String type) {
         LambdaQueryWrapper<MerchantApplyFile> queryWrapper1 = Wrappers.lambdaQuery();
-        queryWrapper1.eq(MerchantApplyFile::getMerchantId, merchant.getMerchantId());
+        queryWrapper1.eq(MerchantApplyFile::getMerchantId, merchantApplyFile.getMerchantId());
         queryWrapper1.eq(MerchantApplyFile::getFileType, type);
-        MerchantApplyFile merchantApplyFiles = merchantApplyFileMapper.selectOne(queryWrapper1);
-        if (merchantApplyFiles == null) {
+        List<MerchantApplyFile> merchantApplyFiles = merchantApplyFileMapper.selectList(queryWrapper1);
+        if (merchantApplyFiles == null || merchantApplyFiles.isEmpty()) {
             return null;
         }
-        return merchantApplyFiles.getFileUrl();
+        return merchantApplyFiles.stream().map(MerchantApplyFile::getFileUrl).collect(Collectors.toList());
     }
 
     /**
@@ -1456,7 +1512,7 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
     /**
      * 获取商户的累计工作时长
      *
-     * @param userId 技师ID
+     * @param userId              技师ID
      * @param excludeAttendanceId 排除的考勤记录ID
      * @return 工作时长(分钟)
      */
@@ -1515,6 +1571,7 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
 
     /**
      * 获取商户信息
+     *
      * @param userId 商户ID
      * @return MaTechnician 商户信息
      */

+ 74 - 35
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TechnicianMomentServiceImpl.java

@@ -228,9 +228,7 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
      * @return Map<String, TJs> 技师ID-技师实体映射
      */
     private Map<Integer, MaTechnician> getTechnicianMap(List<Integer> technicianIds) {
-        List<Integer> ids = technicianIds.stream()
-                .collect(Collectors.toList());
-        return maTechnicianMapper.selectBatchIds(ids).stream().collect(Collectors.toMap(MaTechnician::getId, t -> t));
+        return maTechnicianMapper.selectBatchIds(technicianIds).stream().collect(Collectors.toMap(MaTechnician::getId, t -> t));
     }
 
     /**
@@ -364,7 +362,7 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
         }
 
         // 设置初始状态
-        moment.setStatus(1); // 正常
+        moment.setIsDelete(0);
         moment.setViewCount(0);
         moment.setCreateTime(LocalDateTime.now());
         moment.setUpdateTime(LocalDateTime.now());
@@ -431,12 +429,12 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
             throw new ServiceException("动态ID不能为空");
         }
 
-        // 2. 根据openId查询技师信息
-        LambdaQueryWrapper<TJs> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(TJs::getcOpenId, openId);
-        TJs technician = tJsMapper.selectOne(queryWrapper);
+        // 2. 根据openId查询商户信息
+        LambdaQueryWrapper<MaTechnician> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(MaTechnician::getCOpenid, openId);
+        MaTechnician technician = maTechnicianMapper.selectOne(queryWrapper);
         if (technician == null) {
-            throw new ServiceException("技师信息不存在");
+            throw new ServiceException("商户信息不存在");
         }
 
         // 3. 查询动态信息
@@ -445,14 +443,13 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
             throw new ServiceException("动态不存在");
         }
 
-        // 4. 验证动态是否属于当前技师
+        // 4. 验证动态是否属于当前商户
         if (!moment.getTechnicianId().equals(technician.getId())) {
             throw new ServiceException("无权编辑此动态");
         }
 
         // 5. 验证动态状态是否允许编辑(只能编辑草稿和审核拒绝的动态)
-        if (moment.getAuditStatus() == null ||
-                (moment.getAuditStatus() != 0 && moment.getAuditStatus() != 3)) {
+        if (moment.getAuditStatus() == null || (moment.getAuditStatus() != 0 && moment.getAuditStatus() != 3)) {
             throw new ServiceException("只能编辑草稿或审核拒绝的动态");
         }
 
@@ -477,22 +474,17 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
             throw new ServiceException("媒体类型不正确");
         }
 
-        // 7. 校验可见范围
-        if (dto.getVisibleRange() == null || dto.getVisibleRange() != 1) {
-            throw new ServiceException("可见范围只能选择公开");
-        }
 
-        // 8. 更新动态对象
+        // 7. 更新动态对象
         moment.setTitle(dto.getTitle());
         moment.setContent(dto.getContent());
         moment.setMediaType(dto.getMediaType());
-        moment.setVisibleRange(dto.getVisibleRange());
         moment.setLongitude(dto.getLongitude());
         moment.setLatitude(dto.getLatitude());
         moment.setLocation(dto.getLocation());
         moment.setAddress(dto.getAddress());
 
-        // 设置封面图
+        // 8.设置封面图
         if (dto.getMediaType() == 1) {
             // 图片类型,默认取第一张作为封面
             if (dto.getImageUrls() != null && !dto.getImageUrls().isEmpty()) {
@@ -594,7 +586,6 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
         LambdaQueryWrapper<TechnicianMoment> momentWrapper = new LambdaQueryWrapper<>();
         momentWrapper.eq(TechnicianMoment::getTechnicianId, technician.getId())
                 .eq(TechnicianMoment::getAuditStatus, 0) // 草稿状态
-                .eq(TechnicianMoment::getStatus, 1) // 正常状态
                 .orderByDesc(TechnicianMoment::getCreateTime);
 
         Page<TechnicianMoment> momentPage = momentMapper.selectPage(page, momentWrapper);
@@ -650,7 +641,6 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
         LambdaQueryWrapper<TechnicianMoment> momentWrapper = new LambdaQueryWrapper<>();
         momentWrapper.eq(TechnicianMoment::getTechnicianId, technician.getId())
                 .in(TechnicianMoment::getAuditStatus, 1, 2) // 1-待审核, 2-审核通过
-                .eq(TechnicianMoment::getStatus, 1) // 正常状态
                 .orderByDesc(TechnicianMoment::getPublishTime);
 
         Page<TechnicianMoment> momentPage = momentMapper.selectPage(page, momentWrapper);
@@ -703,7 +693,6 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
         LambdaQueryWrapper<TechnicianMoment> momentWrapper = new LambdaQueryWrapper<>();
         momentWrapper.eq(TechnicianMoment::getTechnicianId, technicianId)
                 .in(TechnicianMoment::getAuditStatus, 1, 2) // 1-待审核, 2-审核通过
-                .eq(TechnicianMoment::getStatus, 1) // 正常状态
                 .orderByDesc(TechnicianMoment::getPublishTime);
 
         List<TechnicianMoment> moments = momentMapper.selectList(momentWrapper);
@@ -750,7 +739,6 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
 
         // 2. 构建查询条件
         LambdaQueryWrapper<TechnicianMoment> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(TechnicianMoment::getStatus, 1); // 只查询正常状态的动态
 
         // 如果传入了审核状态,则按照审核状态查询
         if (auditStatus != null) {
@@ -761,16 +749,16 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
         }
 
         // 技师姓名查询
-        if (technicianName != null && !technicianName.trim().isEmpty()) {
+        if (StringUtils.isNotBlank(technicianName)) {
             //根据技师姓名查询技师的ID
-            LambdaQueryWrapper<TJs> jsQueryWrapper = new LambdaQueryWrapper<>();
-            jsQueryWrapper.like(TJs::getcNickName, technicianName);
-            List<TJs> technicians = tJsMapper.selectList(jsQueryWrapper);
+            LambdaQueryWrapper<MaTechnician> jsQueryWrapper = new LambdaQueryWrapper<>();
+            jsQueryWrapper.like(MaTechnician::getTeName, technicianName);
+            List<MaTechnician> technicians = maTechnicianMapper.selectList(jsQueryWrapper);
             if (CollUtil.isNotEmpty(technicians)) {
-                // 构建查询条件,查询所有匹配的技师的动态
-                queryWrapper.in(TechnicianMoment::getTechnicianId, technicians.stream().map(TJs::getId).collect(Collectors.toList()));
+                // 构建查询条件,查询所有匹配商户的动态
+                queryWrapper.in(TechnicianMoment::getTechnicianId, technicians.stream().map(MaTechnician::getId).collect(Collectors.toList()));
             } else {
-                // 没有匹配的技师,查询空字符串列表
+                // 没有匹配的商户,查询空字符串列表
                 queryWrapper.in(TechnicianMoment::getTechnicianId, Collections.singleton(""));
             }
         }
@@ -832,7 +820,7 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
      * @param momentId     动态ID
      * @param auditStatus  审核状态:2-通过,3-拒绝
      * @param rejectReason 拒绝原因(拒绝时必填)
-     * @return 是否成功
+     * @return Boolean 是否成功
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -855,15 +843,17 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
         }
 
         // 3. 校验动态状态
-        if (moment.getStatus() != 1) {
-            throw new ServiceException("动态已删除,无法审核");
-        }
         if (moment.getAuditStatus() != 1) {
             throw new ServiceException("只能审核待审核状态的动态");
         }
 
         // 4. 更新审核状态
         moment.setAuditStatus(dto.getAuditStatus());
+        // 审核通过默认上架状态和推荐状态
+        if(dto.getAuditStatus()==2){
+            moment.setIsRecommend(1);
+            moment.setIsList(1);
+        }
         moment.setRejectReason(dto.getRejectReason());
         moment.setUpdateTime(LocalDateTime.now());
 
@@ -871,7 +861,56 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
         if (updateResult <= 0) {
             throw new ServiceException("审核失败");
         }
-        log.info("审核动态成功,动态ID:{},审核结果:{},拒绝原因:{}", dto.getMomentId(), dto.getAuditStatus(), dto.getRejectReason());
+        return true;
+    }
+
+    /**
+     * 更新动态推荐状态
+     *
+     * @param momentId    动态ID
+     * @param isRecommend 是否推荐:0-否,1-是
+     * @return 是否成功
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateRecommendStatus(Long momentId, Integer isRecommend) {
+        if (momentId == null) {
+            throw new ServiceException("动态ID不能为空");
+        }
+        if (isRecommend == null || (isRecommend != 0 && isRecommend != 1)) {
+            throw new ServiceException("是否推荐参数不正确");
+        }
+
+        int rows = momentMapper.updateRecommendStatus(momentId, isRecommend);
+        if (rows <= 0) {
+            throw new ServiceException("动态不存在或已删除");
+        }
+        log.info("更新动态推荐状态成功,动态ID:{},是否推荐:{}", momentId, isRecommend);
+        return true;
+    }
+
+    /**
+     * 更新动态上架状态
+     *
+     * @param momentId 动态ID
+     * @param isList   是否上架:0-否,1-是
+     * @return 是否成功
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateListStatus(Long momentId, Integer isList) {
+        if (momentId == null) {
+            throw new ServiceException("动态ID不能为空");
+        }
+        if (isList == null || (isList != 0 && isList != 1)) {
+            throw new ServiceException("上架状态参数不正确");
+        }
+
+        int rows = momentMapper.updateListStatus(momentId, isList);
+        if (rows <= 0) {
+            throw new ServiceException("动态不存在或已删除");
+        }
+        log.info("更新动态上架状态成功,动态ID:{},是否上架:{}", momentId, isList);
         return true;
     }
 

+ 32 - 1
nightFragrance-massage/src/main/resources/mapper/massage/TechnicianMomentMapper.xml

@@ -21,11 +21,13 @@
         <result column="audit_status" property="auditStatus"/>
         <result column="reject_reason" property="rejectReason"/>
         <result column="visible_range" property="visibleRange"/>
+        <result column="is_recommend" property="isRecommend"/>
+        <result column="is_list" property="isList"/>
         <result column="address" property="address"/>
         <result column="poi_name" property="poiName"/>
     </resultMap>
 
-    <!-- 查询推荐动态:按日期倒序,同一天按浏览量倒序 -->
+    <!-- 查询推荐动态:只展示后台标记推荐的公开审核通过动态 -->
     <select id="selectRecommendedMoments" resultMap="BaseResultMap">
         SELECT
             tm.*
@@ -35,6 +37,8 @@
             tm.status = 1
             AND tm.audit_status = 2
             AND tm.visible_range = 1
+            AND tm.is_recommend = 1
+            AND tm.is_list = 1
         ORDER BY
             tm.view_count DESC,
             tm.publish_time DESC
@@ -50,6 +54,7 @@
             tm.status = 1
             AND tm.audit_status = 2
             AND tm.visible_range = 1
+            AND tm.is_list = 1
             AND tm.city_code = #{cityCode}
         ORDER BY
             tm.publish_time DESC
@@ -69,6 +74,7 @@
             tm.status = 1
             AND tm.audit_status = 2
             AND tm.visible_range = 1
+            AND tm.is_list = 1
             AND tm.latitude IS NOT NULL
             AND tm.longitude IS NOT NULL
             AND ST_Distance_Sphere(
@@ -90,6 +96,30 @@
             id = #{momentId} and status = 1 and audit_status = 2
     </update>
 
+    <!-- 更新动态推荐状态 -->
+    <update id="updateRecommendStatus">
+        UPDATE
+            t_technician_moment
+        SET
+            is_recommend = #{isRecommend},
+            update_time = NOW()
+        WHERE
+            id = #{momentId}
+            AND status = 1
+    </update>
+
+    <!-- 更新动态上架状态 -->
+    <update id="updateListStatus">
+        UPDATE
+            t_technician_moment
+        SET
+            is_list = #{isList},
+            update_time = NOW()
+        WHERE
+            id = #{momentId}
+            AND status = 1
+    </update>
+
     <!-- 根据动态ID查询动态简要详情(包含媒体URL列表) -->
     <select id="selectMomentSimpleDetail" resultType="java.util.HashMap">
         SELECT
@@ -123,6 +153,7 @@
           AND status = 1           -- 仅查询正常状态的动态
           AND audit_status = 2     -- 仅查询审核通过的动态
           AND visible_range = 1    -- 仅查询可见范围的动态
+          AND is_list = 1          -- 仅查询已上架的动态
         ORDER BY publish_time DESC
     </select>
 

+ 82 - 0
nightFragrance-massage/src/test/java/com/ylx/massage/mapper/TechnicianMomentMapperXmlTest.java

@@ -0,0 +1,82 @@
+package com.ylx.massage.mapper;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class TechnicianMomentMapperXmlTest {
+
+    @Test
+    public void baseResultMapMapsRecommendStatus() throws Exception {
+        String xml = readMapperXml();
+
+        assertTrue(xml.contains("column=\"is_recommend\" property=\"isRecommend\""));
+    }
+
+    @Test
+    public void baseResultMapMapsListStatus() throws Exception {
+        String xml = readMapperXml();
+
+        assertTrue(xml.contains("column=\"is_list\" property=\"isList\""));
+    }
+
+    @Test
+    public void recommendedMomentsOnlyReturnMarkedRecommendations() throws Exception {
+        String xml = readMapperXml();
+
+        assertTrue(xml.contains("selectRecommendedMoments"));
+        assertTrue(xml.contains("AND tm.audit_status = 2"));
+        assertTrue(xml.contains("AND tm.visible_range = 1"));
+        assertTrue(xml.contains("AND tm.is_recommend = 1"));
+        assertTrue(xml.contains("AND tm.is_list = 1"));
+    }
+
+    @Test
+    public void updateRecommendStatusOnlyUpdatesNormalMoment() throws Exception {
+        String xml = readMapperXml();
+
+        assertTrue(xml.contains("update id=\"updateRecommendStatus\""));
+        assertTrue(xml.contains("is_recommend = #{isRecommend}"));
+        assertTrue(xml.contains("update_time = NOW()"));
+        assertTrue(xml.contains("WHERE"));
+        assertTrue(xml.contains("id = #{momentId}"));
+        assertTrue(xml.contains("AND status = 1"));
+    }
+
+    @Test
+    public void updateListStatusOnlyUpdatesNormalMoment() throws Exception {
+        String xml = readMapperXml();
+
+        assertTrue(xml.contains("update id=\"updateListStatus\""));
+        assertTrue(xml.contains("is_list = #{isList}"));
+        assertTrue(xml.contains("update_time = NOW()"));
+        assertTrue(xml.contains("WHERE"));
+        assertTrue(xml.contains("id = #{momentId}"));
+        assertTrue(xml.contains("AND status = 1"));
+    }
+
+    @Test
+    public void publicMomentQueriesOnlyReturnListedMoments() throws Exception {
+        String xml = readMapperXml();
+
+        assertTrue(xml.contains("selectSameCityMoments"));
+        assertTrue(xml.contains("selectNearbyMoments"));
+        assertTrue(xml.contains("getMerchantProject"));
+        assertTrue(xml.split("AND tm.is_list = 1", -1).length - 1 >= 3);
+        assertTrue(xml.contains("AND is_list = 1"));
+    }
+
+    private String readMapperXml() throws Exception {
+        InputStream inputStream = getClass().getClassLoader()
+                .getResourceAsStream("mapper/massage/TechnicianMomentMapper.xml");
+        assertNotNull(inputStream, "TechnicianMomentMapper.xml should exist");
+        byte[] bytes = new byte[inputStream.available()];
+        int read = inputStream.read(bytes);
+        assertTrue(read > 0, "TechnicianMomentMapper.xml should not be empty");
+        return new String(bytes, StandardCharsets.UTF_8);
+    }
+}

+ 191 - 0
nightFragrance-massage/src/test/java/com/ylx/massage/service/impl/MaTechnicianServiceImplTest.java

@@ -0,0 +1,191 @@
+package com.ylx.massage.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import com.ylx.massage.domain.MaTechnician;
+import com.ylx.massage.domain.MerchantApplyFile;
+import com.ylx.massage.domain.dto.MerchantApplyFileDto;
+import com.ylx.massage.domain.dto.MerchantApplyFileRequestDto;
+import com.ylx.massage.mapper.MaTechnicianMapper;
+import com.ylx.massage.mapper.MerchantApplyFileMapper;
+import org.apache.ibatis.builder.MapperBuilderAssistant;
+import org.apache.ibatis.session.Configuration;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class MaTechnicianServiceImplTest {
+
+    @Test
+    public void updateTechnicianAcceptsGroupedApplyFiles() {
+        initTableInfo(MaTechnician.class);
+        initTableInfo(MerchantApplyFile.class);
+
+        MaTechnicianMapper technicianMapper = mock(MaTechnicianMapper.class);
+        MerchantApplyFileMapper applyFileMapper = mock(MerchantApplyFileMapper.class);
+        when(technicianMapper.update(isNull(), any(Wrapper.class))).thenReturn(1);
+        when(applyFileMapper.delete(any(Wrapper.class))).thenReturn(1);
+
+        MaTechnicianServiceImpl service = new MaTechnicianServiceImpl();
+        ReflectionTestUtils.setField(service, "maTechnicianMapper", technicianMapper);
+        ReflectionTestUtils.setField(service, "merchantApplyFileMapper", applyFileMapper);
+
+        MerchantApplyFileRequestDto request = new MerchantApplyFileRequestDto();
+        MaTechnician technician = new MaTechnician();
+        technician.setId(11);
+        technician.setTeNickName("Ctrlv");
+        technician.setTeBrief("飞洒范德萨");
+        request.setTechnician(technician);
+
+        MerchantApplyFileDto group = new MerchantApplyFileDto();
+        group.setFileType("1");
+        MerchantApplyFileDto file = new MerchantApplyFileDto();
+        file.setFileName("形象照");
+        file.setFileUrl("http://192.168.1.190:8087/profile/upload/2026/06/22/1782124578606_20260622183619A003.jpeg");
+        file.setContentType("jpeg");
+        file.setFileSize(new BigDecimal("54.45"));
+        group.setFiles(Collections.singletonList(file));
+        request.setReq(Collections.singletonList(group));
+
+        service.updateTechnician(request);
+
+        ArgumentCaptor<MerchantApplyFile> fileCaptor = ArgumentCaptor.forClass(MerchantApplyFile.class);
+        verify(applyFileMapper).insert(fileCaptor.capture());
+        MerchantApplyFile savedFile = fileCaptor.getValue();
+        assertEquals(11, savedFile.getMerchantId());
+        assertEquals("1", savedFile.getFileType());
+        assertEquals("形象照", savedFile.getFileName());
+        assertEquals(file.getFileUrl(), savedFile.getFileUrl());
+        assertNull(savedFile.getId());
+    }
+
+    @Test
+    public void updateTechnicianReplacesMultipleFilesOfSameType() {
+        initTableInfo(MaTechnician.class);
+        initTableInfo(MerchantApplyFile.class);
+
+        MaTechnicianMapper technicianMapper = mock(MaTechnicianMapper.class);
+        MerchantApplyFileMapper applyFileMapper = mock(MerchantApplyFileMapper.class);
+        when(technicianMapper.update(isNull(), any(Wrapper.class))).thenReturn(1);
+        when(applyFileMapper.delete(any(Wrapper.class))).thenReturn(2);
+
+        MaTechnicianServiceImpl service = new MaTechnicianServiceImpl();
+        ReflectionTestUtils.setField(service, "maTechnicianMapper", technicianMapper);
+        ReflectionTestUtils.setField(service, "merchantApplyFileMapper", applyFileMapper);
+
+        MerchantApplyFileRequestDto request = new MerchantApplyFileRequestDto();
+        MaTechnician technician = new MaTechnician();
+        technician.setId(11);
+        technician.setTeNickName("Ctrlv");
+        technician.setTeBrief("飞洒范德萨");
+        request.setTechnician(technician);
+
+        MerchantApplyFileDto group = new MerchantApplyFileDto();
+        group.setFileType("2");
+        group.setFiles(Arrays.asList(
+                buildApplyFile("生活照1", "http://192.168.1.190:8087/profile/upload/2026/06/03/life1.png"),
+                buildApplyFile("生活照2", "http://192.168.1.190:8087/profile/upload/2026/06/03/life2.png")
+        ));
+        request.setReq(Collections.singletonList(group));
+
+        service.updateTechnician(request);
+
+        verify(applyFileMapper, times(1)).delete(any(Wrapper.class));
+        verify(applyFileMapper, never()).updateById(any(MerchantApplyFile.class));
+        ArgumentCaptor<MerchantApplyFile> fileCaptor = ArgumentCaptor.forClass(MerchantApplyFile.class);
+        verify(applyFileMapper, times(2)).insert(fileCaptor.capture());
+        List<MerchantApplyFile> savedFiles = fileCaptor.getAllValues();
+        assertEquals("生活照1", savedFiles.get(0).getFileName());
+        assertEquals("生活照2", savedFiles.get(1).getFileName());
+        assertEquals("2", savedFiles.get(0).getFileType());
+        assertEquals("2", savedFiles.get(1).getFileType());
+        assertEquals(11, savedFiles.get(0).getMerchantId());
+        assertEquals(11, savedFiles.get(1).getMerchantId());
+        assertNull(savedFiles.get(0).getId());
+        assertNull(savedFiles.get(1).getId());
+    }
+
+    @Test
+    public void updateTechnicianRejectsBlankNickName() {
+        MaTechnicianServiceImpl service = new MaTechnicianServiceImpl();
+        MerchantApplyFileRequestDto request = buildBaseRequest();
+        request.getTechnician().setTeNickName(" ");
+
+        RuntimeException exception = assertThrows(RuntimeException.class, () -> service.updateTechnician(request));
+
+        assertEquals("昵称不能为空", exception.getMessage());
+    }
+
+    @Test
+    public void updateTechnicianRejectsBlankBrief() {
+        MaTechnicianServiceImpl service = new MaTechnicianServiceImpl();
+        MerchantApplyFileRequestDto request = buildBaseRequest();
+        request.getTechnician().setTeBrief(" ");
+
+        RuntimeException exception = assertThrows(RuntimeException.class, () -> service.updateTechnician(request));
+
+        assertEquals("简介不能为空", exception.getMessage());
+    }
+
+    @Test
+    public void updateTechnicianRejectsEmptyFileGroup() {
+        initTableInfo(MaTechnician.class);
+
+        MaTechnicianMapper technicianMapper = mock(MaTechnicianMapper.class);
+        when(technicianMapper.update(isNull(), any(Wrapper.class))).thenReturn(1);
+        MaTechnicianServiceImpl service = new MaTechnicianServiceImpl();
+        ReflectionTestUtils.setField(service, "maTechnicianMapper", technicianMapper);
+
+        MerchantApplyFileRequestDto request = buildBaseRequest();
+        MerchantApplyFileDto group = new MerchantApplyFileDto();
+        group.setFileType("2");
+        group.setFiles(Collections.emptyList());
+        request.setReq(Collections.singletonList(group));
+
+        RuntimeException exception = assertThrows(RuntimeException.class, () -> service.updateTechnician(request));
+
+        assertEquals("文件列表不能为空", exception.getMessage());
+    }
+
+    private MerchantApplyFileRequestDto buildBaseRequest() {
+        MerchantApplyFileRequestDto request = new MerchantApplyFileRequestDto();
+        MaTechnician technician = new MaTechnician();
+        technician.setId(11);
+        technician.setTeNickName("Ctrlv");
+        technician.setTeBrief("飞洒范德萨");
+        request.setTechnician(technician);
+        return request;
+    }
+
+    private MerchantApplyFileDto buildApplyFile(String fileName, String fileUrl) {
+        MerchantApplyFileDto file = new MerchantApplyFileDto();
+        file.setFileName(fileName);
+        file.setFileUrl(fileUrl);
+        file.setContentType("image/png");
+        file.setFileSize(new BigDecimal("0.73"));
+        return file;
+    }
+
+    private void initTableInfo(Class<?> entityClass) {
+        if (TableInfoHelper.getTableInfo(entityClass) == null) {
+            MapperBuilderAssistant assistant = new MapperBuilderAssistant(new Configuration(), "");
+            TableInfoHelper.initTableInfo(assistant, entityClass);
+        }
+    }
+}

+ 108 - 0
nightFragrance-massage/src/test/java/com/ylx/massage/service/impl/TechnicianMomentServiceImplTest.java

@@ -0,0 +1,108 @@
+package com.ylx.massage.service.impl;
+
+import com.ylx.common.exception.ServiceException;
+import com.ylx.massage.mapper.TechnicianMomentMapper;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class TechnicianMomentServiceImplTest {
+
+    @Test
+    public void updateRecommendStatusRejectsEmptyMomentId() {
+        TechnicianMomentServiceImpl service = new TechnicianMomentServiceImpl();
+
+        ServiceException exception = assertThrows(ServiceException.class,
+                () -> service.updateRecommendStatus(null, 1));
+
+        assertEquals("动态ID不能为空", exception.getMessage());
+    }
+
+    @Test
+    public void updateRecommendStatusRejectsInvalidRecommendStatus() {
+        TechnicianMomentServiceImpl service = new TechnicianMomentServiceImpl();
+
+        ServiceException exception = assertThrows(ServiceException.class,
+                () -> service.updateRecommendStatus(1L, 2));
+
+        assertEquals("是否推荐参数不正确", exception.getMessage());
+    }
+
+    @Test
+    public void updateRecommendStatusDelegatesToMapper() {
+        TechnicianMomentMapper mapper = mock(TechnicianMomentMapper.class);
+        when(mapper.updateRecommendStatus(10L, 1)).thenReturn(1);
+        TechnicianMomentServiceImpl service = new TechnicianMomentServiceImpl();
+        ReflectionTestUtils.setField(service, "momentMapper", mapper);
+
+        Boolean result = service.updateRecommendStatus(10L, 1);
+
+        assertTrue(result);
+        verify(mapper).updateRecommendStatus(10L, 1);
+    }
+
+    @Test
+    public void updateRecommendStatusRejectsMissingMoment() {
+        TechnicianMomentMapper mapper = mock(TechnicianMomentMapper.class);
+        when(mapper.updateRecommendStatus(10L, 1)).thenReturn(0);
+        TechnicianMomentServiceImpl service = new TechnicianMomentServiceImpl();
+        ReflectionTestUtils.setField(service, "momentMapper", mapper);
+
+        ServiceException exception = assertThrows(ServiceException.class,
+                () -> service.updateRecommendStatus(10L, 1));
+
+        assertEquals("动态不存在或已删除", exception.getMessage());
+    }
+
+    @Test
+    public void updateListStatusRejectsEmptyMomentId() {
+        TechnicianMomentServiceImpl service = new TechnicianMomentServiceImpl();
+
+        ServiceException exception = assertThrows(ServiceException.class,
+                () -> service.updateListStatus(null, 1));
+
+        assertEquals("动态ID不能为空", exception.getMessage());
+    }
+
+    @Test
+    public void updateListStatusRejectsInvalidListStatus() {
+        TechnicianMomentServiceImpl service = new TechnicianMomentServiceImpl();
+
+        ServiceException exception = assertThrows(ServiceException.class,
+                () -> service.updateListStatus(1L, 2));
+
+        assertEquals("上架状态参数不正确", exception.getMessage());
+    }
+
+    @Test
+    public void updateListStatusDelegatesToMapper() {
+        TechnicianMomentMapper mapper = mock(TechnicianMomentMapper.class);
+        when(mapper.updateListStatus(10L, 1)).thenReturn(1);
+        TechnicianMomentServiceImpl service = new TechnicianMomentServiceImpl();
+        ReflectionTestUtils.setField(service, "momentMapper", mapper);
+
+        Boolean result = service.updateListStatus(10L, 1);
+
+        assertTrue(result);
+        verify(mapper).updateListStatus(10L, 1);
+    }
+
+    @Test
+    public void updateListStatusRejectsMissingMoment() {
+        TechnicianMomentMapper mapper = mock(TechnicianMomentMapper.class);
+        when(mapper.updateListStatus(10L, 1)).thenReturn(0);
+        TechnicianMomentServiceImpl service = new TechnicianMomentServiceImpl();
+        ReflectionTestUtils.setField(service, "momentMapper", mapper);
+
+        ServiceException exception = assertThrows(ServiceException.class,
+                () -> service.updateListStatus(10L, 1));
+
+        assertEquals("动态不存在或已删除", exception.getMessage());
+    }
+}