Преглед на файлове

开发动态相关的接口

jinshihui преди 1 месец
родител
ревизия
4083dc3ed0
променени са 20 файла, в които са добавени 987 реда и са изтрити 127 реда
  1. 21 3
      nightFragrance-admin/src/main/java/com/ylx/web/controller/common/CommonController.java
  2. 125 7
      nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TechnicianMomentController.java
  3. 36 39
      nightFragrance-common/src/main/java/com/ylx/common/config/RuoYiConfig.java
  4. 29 58
      nightFragrance-common/src/main/java/com/ylx/common/utils/ServletUtils.java
  5. 6 2
      nightFragrance-common/src/main/java/com/ylx/common/utils/file/FileUploadUtils.java
  6. 1 1
      nightFragrance-common/src/main/java/com/ylx/common/utils/file/FileUtils.java
  7. 2 1
      nightFragrance-framework/src/main/java/com/ylx/framework/config/ServerConfig.java
  8. 4 1
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TbFile.java
  9. 1 5
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TechnicianMoment.java
  10. 28 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MomentAuditDTO.java
  11. 49 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MomentManageQueryDTO.java
  12. 98 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/PublishMomentDTO.java
  13. 4 2
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MomentListVO.java
  14. 69 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MomentManageVO.java
  15. 45 0
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MomentSimpleDetailVO.java
  16. 9 0
      nightFragrance-massage/src/main/java/com/ylx/massage/mapper/TechnicianMomentMapper.java
  17. 52 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/ITechnicianMomentService.java
  18. 5 1
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TbFileServiceImpl.java
  19. 385 6
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TechnicianMomentServiceImpl.java
  20. 18 1
      nightFragrance-massage/src/main/resources/mapper/massage/TechnicianMomentMapper.xml

+ 21 - 3
nightFragrance-admin/src/main/java/com/ylx/web/controller/common/CommonController.java

@@ -89,20 +89,36 @@ public class CommonController {
     @ApiOperation("通用上传请求(单个)")
     @PostMapping("/upload")
     public AjaxResult uploadFile(MultipartFile file) throws Exception {
-        return fileService.uploadFile(file);
+        try {
+            return fileService.uploadFile(file);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
     }
 
     /**
-     * 通用上传请求(单个)
+     * 通用上传视频请求(单个)
+     *
+     * @param video 上传的视频文件
+     * @return AjaxResult
      */
     @ApiOperation("通用上传视频请求(单个)")
     @PostMapping("/uploadVi")
     public AjaxResult uploadFileVi(MultipartFile video) throws Exception {
-        return fileService.uploadFile(video);
+        try {
+            return fileService.uploadFile(video);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
     }
 
     /**
      * 通用上传请求(多个)
+     *
+     * @param files 上传的文件列表
+     * @return AjaxResult
      */
     @ApiOperation("通用上传请求(多个)")
     @PostMapping("/uploads")
@@ -110,6 +126,8 @@ public class CommonController {
         try {
             // 上传文件路径
             String filePath = RuoYiConfig.getUploadPath();
+            log.info("上传文件路径:{}", filePath);
+
             List<String> urls = new ArrayList<String>();
             List<String> fileNames = new ArrayList<String>();
             List<String> newFileNames = new ArrayList<String>();

+ 125 - 7
nightFragrance-admin/src/main/java/com/ylx/web/controller/massage/TechnicianMomentController.java

@@ -1,17 +1,30 @@
 package com.ylx.web.controller.massage;
 
+import com.ylx.common.annotation.Log;
 import com.ylx.common.core.controller.BaseController;
 import com.ylx.common.core.domain.R;
+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.PublishMomentDTO;
 import com.ylx.massage.domain.vo.MomentDetailVO;
 import com.ylx.massage.domain.vo.MomentListVO;
+import com.ylx.massage.domain.vo.MomentManageVO;
+import com.ylx.massage.domain.vo.MomentSimpleDetailVO;
 import com.ylx.massage.service.ITechnicianMomentService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.math.BigDecimal;
 import java.util.List;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
 /**
  * 技师动态控制器
@@ -90,15 +103,15 @@ public class TechnicianMomentController extends BaseController {
     @GetMapping("/nearby")
     @ApiOperation("获取附近动态列表")
     public R<List<MomentListVO>> getNearbyMoments(
-            @ApiParam("用户经度") @RequestParam java.math.BigDecimal longitude,
-            @ApiParam("用户纬度") @RequestParam java.math.BigDecimal latitude,
             @ApiParam("页码") @RequestParam(defaultValue = "1") Integer pageNum,
-            @ApiParam("每页数量") @RequestParam(defaultValue = "10") Integer pageSize) {
+            @ApiParam("每页数量") @RequestParam(defaultValue = "10") Integer pageSize,
+            @ApiParam("用户经度") @RequestParam BigDecimal longitude,
+            @ApiParam("用户纬度") @RequestParam BigDecimal latitude) {
         try {
             if (latitude == null || longitude == null) {
                 return R.fail("地理位置信息不能为空");
             }
-            List<MomentListVO> list = momentService.getNearbyMoments(latitude, longitude, pageNum, pageSize);
+            List<MomentListVO> list = momentService.getNearbyMoments(longitude, latitude, pageNum, pageSize);
             return R.ok(list);
         } catch (Exception e) {
             log.error("获取附近动态列表失败", e);
@@ -112,12 +125,11 @@ public class TechnicianMomentController extends BaseController {
      * 每点击查看一次,浏览量+1
      *
      * @param momentId 动态ID
-     * @return 动态详情
+     * @return R<MomentDetailVO> 动态详情
      */
     @GetMapping("/detail/{momentId}")
     @ApiOperation("获取动态详情")
-    public R<MomentDetailVO> getMomentDetail(
-            @ApiParam("动态ID") @PathVariable Long momentId) {
+    public R<MomentDetailVO> getMomentDetail(@ApiParam("动态ID") @PathVariable Long momentId) {
         try {
             if (momentId == null) {
                 return R.fail("动态ID不能为空");
@@ -183,4 +195,110 @@ public class TechnicianMomentController extends BaseController {
             return R.fail("查询动态列表失败:" + e.getMessage());
         }
     }
+
+    /**
+     * 发布动态或保存草稿
+     *
+     * @param dto 发布动态请求对象
+     * @return R 动态ID
+     */
+    @PostMapping("/publish")
+    @ApiOperation("发布动态或保存草稿")
+    public R<Long> publishMoment(@Valid @RequestBody PublishMomentDTO dto) {
+        try {
+            // 获取当前登录用户信息
+            WxLoginUser wxLoginUser = getWxLoginUser();
+            String openId = wxLoginUser.getCOpenid();
+            log.info("发布动态,openId:{},标题:{},媒体类型:{},是否草稿:{}", openId, dto.getTitle(), dto.getMediaType(), dto.getIsDraft());
+
+            Long momentId = momentService.publishMoment(dto, openId);
+            return R.ok(momentId, dto.getIsDraft() != null && dto.getIsDraft() == 1 ? "保存草稿成功" : "发布成功,等待审核");
+        } catch (Exception e) {
+            log.error("发布动态失败", e);
+            return R.fail("发布动态失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 查询草稿箱列表
+     *
+     * @param pageNum  页码
+     * @param pageSize 每页数量
+     * @return R
+     */
+    @GetMapping("/drafts")
+    @ApiOperation("查询草稿箱列表")
+    public R<List<MomentListVO>> getDraftMoments(@ApiParam("页码") @RequestParam(defaultValue = "1") Integer pageNum, @ApiParam("每页数量") @RequestParam(defaultValue = "10") Integer pageSize) {
+        try {
+            // 获取当前登录用户信息
+            WxLoginUser wxLoginUser = getWxLoginUser();
+            String openId = wxLoginUser.getCOpenid();
+            log.info("查询草稿箱列表,openId:{}", openId);
+
+            List<MomentListVO> list = momentService.getDraftMoments(openId, pageNum, pageSize);
+            return R.ok(list);
+        } catch (Exception e) {
+            log.error("查询草稿箱列表失败", e);
+            return R.fail("查询草稿箱列表失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 查询动态管理列表(PC管理后台)
+     *
+     * @param queryDTO 查询参数
+     * @return R 分页结果
+     */
+    @GetMapping("/manage/list")
+    @ApiOperation("查询动态管理列表")
+    public R<Page<MomentManageVO>> getMomentManageList(@Valid MomentManageQueryDTO queryDTO) {
+        try {
+            log.info("查询动态管理列表,参数:{}", queryDTO);
+            Page<MomentManageVO> page = momentService.getMomentManageList(queryDTO);
+            return R.ok(page);
+        } catch (Exception e) {
+            log.error("查询动态管理列表失败", e);
+            return R.fail("查询动态管理列表失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 审核动态(通过/拒绝)
+     *
+     * @param dto 动态审核请求对象
+     * @return R 是否成功
+     */
+    @PostMapping("/manage/audit")
+    @ApiOperation("审核动态")
+    public R<Boolean> auditMoment(@Valid @RequestBody MomentAuditDTO dto) {
+        try {
+            log.info("审核动态,动态ID:{},审核状态:{},拒绝原因:{}", dto.getMomentId(), dto.getAuditStatus(), dto.getRejectReason());
+            Boolean result = momentService.auditMoment(dto);
+            String message = dto.getAuditStatus() == 2 ? "审核通过" : "审核拒绝";
+            return R.ok(result, message);
+        } catch (Exception e) {
+            log.error("审核动态失败", e);
+            return R.fail("审核动态失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据动态ID查询动态简要详情
+     *
+     * @param momentId 动态ID
+     * @return R
+     */
+    @GetMapping("/simple/detail/{momentId}")
+    @ApiOperation("根据动态ID查询动态简要详情")
+    public R<MomentSimpleDetailVO> getMomentSimpleDetail(@ApiParam("动态ID") @PathVariable Long momentId) {
+        try {
+            log.info("查询动态简要详情,动态ID:{}", momentId);
+            MomentSimpleDetailVO detail = momentService.getMomentSimpleDetail(momentId);
+            return R.ok(detail);
+        } catch (Exception e) {
+            log.error("查询动态简要详情失败", e);
+            return R.fail("查询动态简要详情失败:" + e.getMessage());
+        }
+    }
+
 }

+ 36 - 39
nightFragrance-common/src/main/java/com/ylx/common/config/RuoYiConfig.java

@@ -10,83 +10,85 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @ConfigurationProperties(prefix = "ylx")
-public class RuoYiConfig
-{
-    /** 项目名称 */
+public class RuoYiConfig {
+    /**
+     * 项目名称
+     */
     private String name;
 
-    /** 版本 */
+    /**
+     * 版本
+     */
     private String version;
 
-    /** 版权年份 */
+    /**
+     * 版权年份
+     */
     private String copyrightYear;
 
-    /** 上传路径 */
+    /**
+     * 上传路径
+     */
     private static String profile;
 
-    /** 获取地址开关 */
+    /**
+     * 获取地址开关
+     */
     private static boolean addressEnabled;
 
-    /** 验证码类型 */
+    /**
+     * 验证码类型
+     */
     private static String captchaType;
 
 
-
     private static String ym;
 
     public String getYm() {
         return ym;
     }
+
     public void setYm(String ym) {
         this.ym = ym;
     }
-    public String getName()
-    {
+
+    public String getName() {
         return name;
     }
 
-    public void setName(String name)
-    {
+    public void setName(String name) {
         this.name = name;
     }
 
-    public String getVersion()
-    {
+    public String getVersion() {
         return version;
     }
 
-    public void setVersion(String version)
-    {
+    public void setVersion(String version) {
         this.version = version;
     }
 
-    public String getCopyrightYear()
-    {
+    public String getCopyrightYear() {
         return copyrightYear;
     }
 
-    public void setCopyrightYear(String copyrightYear)
-    {
+    public void setCopyrightYear(String copyrightYear) {
         this.copyrightYear = copyrightYear;
     }
 
-    public static String getProfile()
-    {
+    public static String getProfile() {
         return profile;
     }
 
-    public void setProfile(String profile)
-    {
+    public void setProfile(String profile) {
         RuoYiConfig.profile = profile;
     }
 
-    public static boolean isAddressEnabled()
-    {
+    public static boolean isAddressEnabled() {
         return addressEnabled;
     }
 
-    public void setAddressEnabled(boolean addressEnabled)
-    {
+    public void setAddressEnabled(boolean addressEnabled) {
         RuoYiConfig.addressEnabled = addressEnabled;
     }
 
@@ -101,40 +103,35 @@ public class RuoYiConfig
     /**
      * 获取导入上传路径
      */
-    public static String getImportPath()
-    {
+    public static String getImportPath() {
         return getProfile() + "/import";
     }
 
     /**
      * 获取头像上传路径
      */
-    public static String getAvatarPath()
-    {
+    public static String getAvatarPath() {
         return getProfile() + "/avatar";
     }
 
     /**
      * 获取下载路径
      */
-    public static String getDownloadPath()
-    {
+    public static String getDownloadPath() {
         return getProfile() + "/download/";
     }
 
     /**
      * 获取上传路径
      */
-    public static String getUploadPath()
-    {
+    public static String getUploadPath() {
         return getProfile() + "/upload";
     }
 
     /**
      * 获取上传路径
      */
-    public static String getUploadEwmPath()
-    {
+    public static String getUploadEwmPath() {
         return getProfile() + "/ewm/";
     }
 }

+ 29 - 58
nightFragrance-common/src/main/java/com/ylx/common/utils/ServletUtils.java

@@ -11,6 +11,7 @@ import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+
 import org.springframework.web.context.request.RequestAttributes;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
@@ -22,53 +23,46 @@ import com.ylx.common.core.text.Convert;
  *
  * @author ylx
  */
-public class ServletUtils
-{
+public class ServletUtils {
     /**
      * 获取String参数
      */
-    public static String getParameter(String name)
-    {
+    public static String getParameter(String name) {
         return getRequest().getParameter(name);
     }
 
     /**
      * 获取String参数
      */
-    public static String getParameter(String name, String defaultValue)
-    {
+    public static String getParameter(String name, String defaultValue) {
         return Convert.toStr(getRequest().getParameter(name), defaultValue);
     }
 
     /**
      * 获取Integer参数
      */
-    public static Integer getParameterToInt(String name)
-    {
+    public static Integer getParameterToInt(String name) {
         return Convert.toInt(getRequest().getParameter(name));
     }
 
     /**
      * 获取Integer参数
      */
-    public static Integer getParameterToInt(String name, Integer defaultValue)
-    {
+    public static Integer getParameterToInt(String name, Integer defaultValue) {
         return Convert.toInt(getRequest().getParameter(name), defaultValue);
     }
 
     /**
      * 获取Boolean参数
      */
-    public static Boolean getParameterToBool(String name)
-    {
+    public static Boolean getParameterToBool(String name) {
         return Convert.toBool(getRequest().getParameter(name));
     }
 
     /**
      * 获取Boolean参数
      */
-    public static Boolean getParameterToBool(String name, Boolean defaultValue)
-    {
+    public static Boolean getParameterToBool(String name, Boolean defaultValue) {
         return Convert.toBool(getRequest().getParameter(name), defaultValue);
     }
 
@@ -78,8 +72,7 @@ public class ServletUtils
      * @param request 请求对象{@link ServletRequest}
      * @return Map
      */
-    public static Map<String, String[]> getParams(ServletRequest request)
-    {
+    public static Map<String, String[]> getParams(ServletRequest request) {
         final Map<String, String[]> map = request.getParameterMap();
         return Collections.unmodifiableMap(map);
     }
@@ -90,11 +83,9 @@ public class ServletUtils
      * @param request 请求对象{@link ServletRequest}
      * @return Map
      */
-    public static Map<String, String> getParamMap(ServletRequest request)
-    {
+    public static Map<String, String> getParamMap(ServletRequest request) {
         Map<String, String> params = new HashMap<>();
-        for (Map.Entry<String, String[]> entry : getParams(request).entrySet())
-        {
+        for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
             params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
         }
         return params;
@@ -103,29 +94,25 @@ public class ServletUtils
     /**
      * 获取request
      */
-    public static HttpServletRequest getRequest()
-    {
+    public static HttpServletRequest getRequest() {
         return getRequestAttributes().getRequest();
     }
 
     /**
      * 获取response
      */
-    public static HttpServletResponse getResponse()
-    {
+    public static HttpServletResponse getResponse() {
         return getRequestAttributes().getResponse();
     }
 
     /**
      * 获取session
      */
-    public static HttpSession getSession()
-    {
+    public static HttpSession getSession() {
         return getRequest().getSession();
     }
 
-    public static ServletRequestAttributes getRequestAttributes()
-    {
+    public static ServletRequestAttributes getRequestAttributes() {
         RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
         return (ServletRequestAttributes) attributes;
     }
@@ -134,19 +121,15 @@ public class ServletUtils
      * 将字符串渲染到客户端
      *
      * @param response 渲染对象
-     * @param string 待渲染的字符串
+     * @param string   待渲染的字符串
      */
-    public static void renderString(HttpServletResponse response, String string)
-    {
-        try
-        {
+    public static void renderString(HttpServletResponse response, String string) {
+        try {
             response.setStatus(200);
             response.setContentType("application/json");
             response.setCharacterEncoding("utf-8");
             response.getWriter().print(string);
-        }
-        catch (IOException e)
-        {
+        } catch (IOException e) {
             e.printStackTrace();
         }
     }
@@ -156,23 +139,19 @@ public class ServletUtils
      *
      * @param request
      */
-    public static boolean isAjaxRequest(HttpServletRequest request)
-    {
+    public static boolean isAjaxRequest(HttpServletRequest request) {
         String accept = request.getHeader("accept");
-        if (accept != null && accept.contains("application/json"))
-        {
+        if (accept != null && accept.contains("application/json")) {
             return true;
         }
 
         String xRequestedWith = request.getHeader("X-Requested-With");
-        if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest"))
-        {
+        if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
             return true;
         }
 
         String uri = request.getRequestURI();
-        if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
-        {
+        if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) {
             return true;
         }
 
@@ -186,14 +165,10 @@ public class ServletUtils
      * @param str 内容
      * @return 编码后的内容
      */
-    public static String urlEncode(String str)
-    {
-        try
-        {
+    public static String urlEncode(String str) {
+        try {
             return URLEncoder.encode(str, Constants.UTF8);
-        }
-        catch (UnsupportedEncodingException e)
-        {
+        } catch (UnsupportedEncodingException e) {
             return StringUtils.EMPTY;
         }
     }
@@ -204,14 +179,10 @@ public class ServletUtils
      * @param str 内容
      * @return 解码后的内容
      */
-    public static String urlDecode(String str)
-    {
-        try
-        {
+    public static String urlDecode(String str) {
+        try {
             return URLDecoder.decode(str, Constants.UTF8);
-        }
-        catch (UnsupportedEncodingException e)
-        {
+        } catch (UnsupportedEncodingException e) {
             return StringUtils.EMPTY;
         }
     }

+ 6 - 2
nightFragrance-common/src/main/java/com/ylx/common/utils/file/FileUploadUtils.java

@@ -68,7 +68,7 @@ public class FileUploadUtils {
      *
      * @param baseDir 相对应用的基目录
      * @param file    上传的文件
-     * @return 文件名称
+     * @return String 文件名称
      * @throws IOException
      */
     public static final String upload(String baseDir, MultipartFile file) throws IOException {
@@ -114,7 +114,7 @@ public class FileUploadUtils {
         if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
             throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
         }
-
+        //校验文件大小
         assertAllowed(file, allowedExtension);
 
         String fileName = extractFilename(file);
@@ -126,6 +126,9 @@ public class FileUploadUtils {
 
     /**
      * 编码文件名
+     *
+     * @param file 上传的文件
+     * @return String 文件名
      */
     public static final String extractFilename(MultipartFile file) {
         return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
@@ -153,6 +156,7 @@ public class FileUploadUtils {
      * 文件大小校验
      *
      * @param file 上传的文件
+     * @param allowedExtension 上传文件类型
      * @return
      * @throws FileSizeLimitExceededException 如果超出最大大小
      * @throws InvalidExtensionException

+ 1 - 1
nightFragrance-common/src/main/java/com/ylx/common/utils/file/FileUtils.java

@@ -222,7 +222,7 @@ public class FileUtils {
      * 获取文件名称 /profile/upload/2022/04/16/ylx.png -- ylx.png
      *
      * @param fileName 路径名称
-     * @return 没有文件路径的名称
+     * @return String 没有文件路径的名称
      */
     public static String getName(String fileName) {
         if (fileName == null) {

+ 2 - 1
nightFragrance-framework/src/main/java/com/ylx/framework/config/ServerConfig.java

@@ -15,13 +15,14 @@ public class ServerConfig {
     /**
      * 获取完整的请求路径,包括:域名,端口,上下文访问路径
      *
-     * @return 服务地址
+     * @return String 服务地址
      */
     public String getUrl() {
         HttpServletRequest request = ServletUtils.getRequest();
         return getDomain(request);
     }
 
+
     public static String getDomain(HttpServletRequest request) {
         StringBuffer url = request.getRequestURL();
         String contextPath = request.getServletContext().getContextPath();

+ 4 - 1
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TbFile.java

@@ -19,7 +19,10 @@ import java.util.Date;
 public class TbFile extends Model<TbFile> {
     //文件id
     private String id;
-    //文件md5值
+
+    /**
+     * 文件md5值
+     */
     private String md5;
     //虚拟文件路径
     private String fileUrl;

+ 1 - 5
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TechnicianMoment.java

@@ -85,7 +85,7 @@ public class TechnicianMoment {
     private LocalDateTime updateTime;
 
     /**
-     * 审核状态:0-草稿,1-审核,2-审核通过,3-审核拒绝
+     * 审核状态:0-草稿,1-审核,2-审核通过,3-审核拒绝
      */
     private Integer auditStatus;
 
@@ -104,8 +104,4 @@ public class TechnicianMoment {
      */
     private String address;
 
-    /**
-     * POI名称
-     */
-    private String poiName;
 }

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

@@ -0,0 +1,28 @@
+package com.ylx.massage.domain.dto;
+
+import lombok.Data;
+
+/**
+ * 动态审核DTO
+ */
+@Data
+public class MomentAuditDTO {
+    /**
+     * 动态ID
+     */
+    private Long momentId;
+
+     /**
+      * 审核状态(0-草稿 1-待审核 2-审核通过 3-审核拒绝)
+      */
+    private Integer auditStatus;
+
+     /**
+      * 拒绝原因(拒绝时必填)
+      */
+    private String rejectReason;
+
+}
+
+
+

+ 49 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/MomentManageQueryDTO.java

@@ -0,0 +1,49 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 动态管理查询DTO
+ */
+@Data
+@ApiModel("动态管理查询参数")
+public class MomentManageQueryDTO {
+
+    /**
+     * 页码
+     */
+    @ApiModelProperty("页码")
+    private Integer pageNum;
+
+    /**
+     * 每页数量
+     */
+    @ApiModelProperty("每页数量")
+    private Integer pageSize;
+
+    /**
+     * 技师姓名(模糊查询)
+     */
+    @ApiModelProperty("技师姓名")
+    private String technicianName;
+
+    /**
+     * 开始时间
+     */
+    @ApiModelProperty("开始时间")
+    private String startTime;
+
+    /**
+     * 结束时间
+     */
+    @ApiModelProperty("结束时间")
+    private String endTime;
+
+    /**
+     * 审核状态:0-草稿,1-待审核,2-审核通过,3-审核拒绝
+     */
+    @ApiModelProperty("审核状态:0-草稿,1-待审核,2-审核通过,3-审核拒绝")
+    private Integer auditStatus;
+}

+ 98 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/dto/PublishMomentDTO.java

@@ -0,0 +1,98 @@
+package com.ylx.massage.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 发布动态请求DTO
+ */
+@Data
+@ApiModel("发布动态请求")
+public class PublishMomentDTO {
+
+    /**
+     * 动态标题
+     */
+    @ApiModelProperty(value = "动态标题", required = true)
+    @NotBlank(message = "动态标题不能为空")
+    private String title;
+
+    /**
+     * 动态内容
+     */
+    @ApiModelProperty(value = "动态内容", required = true)
+    @NotBlank(message = "动态内容不能为空")
+    @Size(max = 1000, message = "动态内容不能超过300个字符")
+    private String content;
+
+    /**
+     * 媒体类型:1-图片,2-视频
+     */
+    @ApiModelProperty(value = "媒体类型:1-图片,2-视频", required = true)
+    @NotNull(message = "媒体类型不能为空")
+    private Integer mediaType;
+
+    /**
+     * 图片URL列表(最多9张)
+     */
+    @ApiModelProperty("图片URL列表,最多9张")
+    @Size(max = 9, message = "最多只能上传9张图片")
+    private List<String> imageUrls;
+
+    /**
+     * 视频URL
+     */
+    @ApiModelProperty("视频URL")
+    private String videoUrl;
+
+    /**
+     * 视频封面URL
+     */
+    @ApiModelProperty("视频封面URL")
+    private String videoCoverUrl;
+
+    /**
+     * 可见范围:1-公开
+     */
+    @ApiModelProperty(value = "可见范围:1-公开", required = true)
+    @NotNull(message = "可见范围不能为空")
+    private Integer visibleRange;
+
+    /**
+     * 纬度
+     */
+    @ApiModelProperty("纬度")
+    private BigDecimal latitude;
+
+    /**
+     * 经度
+     */
+    @ApiModelProperty("经度")
+    private BigDecimal longitude;
+
+    /**
+     * 发布地址
+     */
+    @ApiModelProperty("发布地址")
+    private String address;
+
+
+    /**
+     * 城市编码
+     */
+    @ApiModelProperty("城市编码")
+    private String cityCode;
+
+    /**
+     * 是否保存为草稿:0-立即发布,1-保存草稿
+     */
+    @ApiModelProperty("是否保存为草稿:0-立即发布,1-保存草稿")
+    private Integer isDraft;
+}

+ 4 - 2
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MomentListVO.java

@@ -1,5 +1,6 @@
 package com.ylx.massage.domain.vo;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -53,6 +54,7 @@ public class MomentListVO {
      * 动态发布的时间戳
      */
     @ApiModelProperty("发布时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" ,timezone = "GMT+8")
     private LocalDateTime publishTime;
 
     /**
@@ -60,13 +62,13 @@ public class MomentListVO {
      * 发布该动态的技师主键标识
      */
     @ApiModelProperty("技师ID")
-    private Long technicianId;
+    private String technicianId;
 
     /**
      * 技师昵称
      */
     @ApiModelProperty("技师昵称")
-    private String technicianName;
+    private String technicianNickName;
 
     /**
      * 技师头像

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

@@ -0,0 +1,69 @@
+package com.ylx.massage.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 动态管理VO(管理后台用)
+ */
+@Data
+@ApiModel("动态管理信息")
+public class MomentManageVO {
+    /**
+     * 动态ID
+     */
+    @ApiModelProperty("动态ID")
+    private Long id;
+
+    /**
+     * 技师姓名
+     */
+    private String technicianName;
+
+    /**
+     * 技师昵称
+     */
+    @ApiModelProperty("技师昵称")
+    private String technicianNickname;
+
+    /**
+     * 媒体类型:1-图片,2-视频
+     */
+    @ApiModelProperty("媒体类型:1-图片,2-视频")
+    private Integer mediaType;
+
+    /**
+     * 封面图URL
+     */
+    @ApiModelProperty("封面图URL")
+    private String coverUrl;
+
+    /**
+     * 动态标题
+     */
+    @ApiModelProperty("动态标题")
+    private String title;
+
+     /**
+     * 动态内容
+     */
+    @ApiModelProperty("动态内容")
+    private String content;
+
+     /**
+     * 发布时间
+     */
+    @ApiModelProperty("发布时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime publishTime;
+
+    /**
+     * 审核状态:0-草稿,1-待审核,2-审核通过,3-审核拒绝
+     */
+    @ApiModelProperty("审核状态:0-草稿,1-待审核,2-审核通过,3-审核拒绝")
+    private Integer auditStatus;
+}

+ 45 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/domain/vo/MomentSimpleDetailVO.java

@@ -0,0 +1,45 @@
+package com.ylx.massage.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 动态简要详情VO
+ */
+@Data
+@ApiModel("动态简要详情")
+public class MomentSimpleDetailVO {
+
+    /**
+     * 动态ID
+     */
+    @ApiModelProperty("动态ID")
+    private Long id;
+
+    /**
+     * 动态标题
+     */
+    @ApiModelProperty("动态标题")
+    private String title;
+
+    /**
+     * 动态内容
+     */
+    @ApiModelProperty("动态内容")
+    private String content;
+
+    /**
+     * 媒体类型:1-图片,2-视频
+     */
+    @ApiModelProperty("媒体类型:1-图片,2-视频")
+    private Integer mediaType;
+
+    /**
+     * 媒体URL列表
+     */
+    @ApiModelProperty("媒体URL列表")
+    private List<String> mediaUrls;
+}

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

@@ -7,6 +7,7 @@ import org.apache.ibatis.annotations.Param;
 
 import java.math.BigDecimal;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 技师动态Mapper接口
@@ -33,4 +34,12 @@ public interface TechnicianMomentMapper extends BaseMapper<TechnicianMoment> {
      * 增加浏览量
      */
     int incrementViewCount(@Param("momentId") Long momentId);
+
+    /**
+     * 根据动态ID查询动态简要详情(包含媒体URL列表)
+     *
+     * @param momentId 动态ID
+     * @return 动态简要信息(包含媒体URL列表)
+     */
+    Map<String, Object> selectMomentSimpleDetail(@Param("momentId") Long momentId);
 }

+ 52 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/ITechnicianMomentService.java

@@ -1,8 +1,14 @@
 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.dto.MomentAuditDTO;
+import com.ylx.massage.domain.dto.MomentManageQueryDTO;
+import com.ylx.massage.domain.dto.PublishMomentDTO;
 import com.ylx.massage.domain.vo.MomentDetailVO;
 import com.ylx.massage.domain.vo.MomentListVO;
+import com.ylx.massage.domain.vo.MomentManageVO;
+import com.ylx.massage.domain.vo.MomentSimpleDetailVO;
 
 import java.math.BigDecimal;
 import java.util.List;
@@ -49,4 +55,50 @@ public interface ITechnicianMomentService extends IService<com.ylx.massage.domai
      * @return 动态详情
      */
     MomentDetailVO getMomentDetail(Long momentId);
+
+    /**
+     * 发布动态或保存草稿
+     *
+     * @param dto      发布动态请求对象
+     * @param openId   技师OpenID
+     * @return 动态ID
+     */
+    Long publishMoment(PublishMomentDTO dto, String openId);
+
+    /**
+     * 查询草稿箱列表
+     *
+     * @param openId   技师OpenID
+     * @param pageNum  页码
+     * @param pageSize 每页数量
+     * @return 草稿列表
+     */
+    List<MomentListVO> getDraftMoments(String openId, Integer pageNum, Integer pageSize);
+
+    /**
+     * 查询待审核动态列表(管理后台)
+     *
+     * @param queryDTO 查询参数
+     * @return Page<MomentManageVO> 分页结果
+     */
+    Page<MomentManageVO> getMomentManageList(MomentManageQueryDTO queryDTO);
+
+    /**
+     * 审核动态(通过/拒绝)
+     *
+     * @param momentId     动态ID
+     * @param auditStatus  审核状态:2-通过,3-拒绝
+     * @param rejectReason 拒绝原因(拒绝时必填)
+     * @return 是否成功
+     */
+    Boolean auditMoment(MomentAuditDTO dto);
+
+    /**
+     * 根据动态ID查询动态简要详情(包含媒体URL列表)
+     *
+     * @param momentId 动态ID
+     * @return 动态简要详情
+     */
+    MomentSimpleDetailVO getMomentSimpleDetail(Long momentId);
+
 }

+ 5 - 1
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TbFileServiceImpl.java

@@ -11,6 +11,7 @@ import com.ylx.common.utils.file.FileUtils;
 import com.ylx.framework.config.ServerConfig;
 import com.ylx.massage.domain.TbFile;
 import com.ylx.massage.mapper.TbFileMapper;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.ylx.massage.service.TbFileService;
@@ -31,6 +32,7 @@ import java.io.IOException;
  * @since 2024-04-27
  */
 @Service("tbFileService")
+@Slf4j
 public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> implements TbFileService {
 
     @Autowired
@@ -79,7 +81,7 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
      * 4. 如果不存在,将文件上传到服务器并保存文件记录到数据库
      * </p>
      *
-     * @param file 上传的文件对象,包含文件内容和元数据
+     * @param file 上传的文件对象
      * @return AjaxResult 包含文件访问信息的对象:
      *         - url: 文件的完整访问 URL(包含域名)
      *         - fileName: 服务器存储的文件路径
@@ -108,6 +110,8 @@ public class TbFileServiceImpl extends ServiceImpl<TbFileMapper, TbFile> impleme
             // 文件不存在,上传新文件
             // 获取上传文件路径配置
             String filePath = RuoYiConfig.getUploadPath();
+            log.info("上传文件路径:{}", filePath);
+
             // 上传文件到服务器并返回新文件名称
             String fileName = FileUploadUtils.upload(filePath, file);
             // 构建文件访问的完整 URL

+ 385 - 6
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TechnicianMomentServiceImpl.java

@@ -1,26 +1,36 @@
 package com.ylx.massage.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ylx.common.core.domain.model.WxLoginUser;
 import com.ylx.common.exception.ServiceException;
 import com.ylx.massage.domain.MomentMedia;
 import com.ylx.massage.domain.TechnicianMoment;
 import com.ylx.massage.domain.TJs;
+import com.ylx.massage.domain.dto.MomentAuditDTO;
+import com.ylx.massage.domain.dto.MomentManageQueryDTO;
+import com.ylx.massage.domain.dto.PublishMomentDTO;
 import com.ylx.massage.domain.vo.MomentDetailVO;
 import com.ylx.massage.domain.vo.MomentListVO;
+import com.ylx.massage.domain.vo.MomentManageVO;
 import com.ylx.massage.domain.vo.MomentMediaVO;
+import com.ylx.massage.domain.vo.MomentSimpleDetailVO;
 import com.ylx.massage.mapper.MomentMediaMapper;
 import com.ylx.massage.mapper.TechnicianMomentMapper;
 import com.ylx.massage.mapper.TJsMapper;
 import com.ylx.massage.service.ITechnicianMomentService;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -30,8 +40,7 @@ import java.util.stream.Collectors;
  */
 @Service
 @Slf4j
-public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMapper, TechnicianMoment>
-        implements ITechnicianMomentService {
+public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMapper, TechnicianMoment> implements ITechnicianMomentService {
 
     @Autowired
     private TechnicianMomentMapper momentMapper;
@@ -68,7 +77,7 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
 
             TJs technician = technicianMap.get(moment.getTechnicianId());
             if (technician != null) {
-                vo.setTechnicianName(technician.getcNickName());
+                vo.setTechnicianNickName(technician.getcNickName());
                 vo.setTechnicianAvatar(technician.getcPortrait());
                 vo.setTechnicianStatus(getTechnicianStatus(technician));
             }
@@ -98,7 +107,7 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
 
             TJs technician = technicianMap.get(moment.getTechnicianId());
             if (technician != null) {
-                vo.setTechnicianName(technician.getcNickName());
+                vo.setTechnicianNickName(technician.getcNickName());
                 vo.setTechnicianAvatar(technician.getcPortrait());
                 vo.setTechnicianStatus(getTechnicianStatus(technician));
             }
@@ -121,7 +130,7 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
             throw new ServiceException("地理位置信息不能为空");
         }
         Page<TechnicianMoment> page = new Page<>(pageNum, pageSize);
-        List<TechnicianMoment> moments = momentMapper.selectNearbyMoments(page, latitude, longitude);
+        List<TechnicianMoment> moments = momentMapper.selectNearbyMoments(page, longitude, latitude);
         if (moments == null || moments.isEmpty()) {
             return new ArrayList<>();
         }
@@ -140,7 +149,7 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
 
             TJs technician = technicianMap.get(String.valueOf(moment.getTechnicianId()));
             if (technician != null) {
-                vo.setTechnicianName(technician.getcNickName());
+                vo.setTechnicianNickName(technician.getcNickName());
                 vo.setTechnicianAvatar(technician.getcPortrait());
                 vo.setTechnicianStatus(getTechnicianStatus(technician));
 
@@ -160,6 +169,9 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
 
     /**
      * 查询动态详情(浏览量+1)
+     *
+     * @param momentId 动态ID
+     * @return MomentDetailVO 动态详情
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -255,4 +267,371 @@ public class TechnicianMomentServiceImpl extends ServiceImpl<TechnicianMomentMap
 
         return R * c;
     }
+
+    /**
+     * 发布动态或保存草稿
+     *
+     * @param dto    发布动态请求对象
+     * @param openId 技师OpenID
+     * @return Long 动态ID
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long publishMoment(PublishMomentDTO dto, String openId) {
+        // 1. 参数校验
+        if (dto == null) {
+            throw new ServiceException("发布动态信息不能为空");
+        }
+        if (openId == null || openId.trim().isEmpty()) {
+            throw new ServiceException("技师信息不能为空");
+        }
+
+        // 2. 根据openId查询技师信息
+        LambdaQueryWrapper<TJs> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(TJs::getcOpenId, openId);
+        TJs technician = tJsMapper.selectOne(queryWrapper);
+        if (technician == null) {
+            throw new ServiceException("技师信息不存在");
+        }
+
+        // 3. 校验媒体类型和媒体文件
+        if (dto.getMediaType() == null) {
+            throw new ServiceException("媒体类型不能为空");
+        }
+        if (dto.getMediaType() == 1) {
+            // 图片类型
+            if (dto.getImageUrls() == null || dto.getImageUrls().isEmpty()) {
+                throw new ServiceException("图片不能为空");
+            }
+            if (dto.getImageUrls().size() > 9) {
+                throw new ServiceException("最多只能上传9张图片");
+            }
+        } else if (dto.getMediaType() == 2) {
+            // 视频类型
+            if (dto.getVideoUrl() == null || dto.getVideoUrl().trim().isEmpty()) {
+                throw new ServiceException("视频不能为空");
+            }
+        } else {
+            throw new ServiceException("媒体类型不正确");
+        }
+
+        // 4. 校验可见范围
+        if (dto.getVisibleRange() == null || dto.getVisibleRange() != 1) {
+            throw new ServiceException("可见范围只能选择公开");
+        }
+
+        // 5. 创建动态对象
+        TechnicianMoment moment = new TechnicianMoment();
+        moment.setTechnicianId(technician.getId());
+        moment.setTitle(dto.getTitle());
+        moment.setContent(dto.getContent());
+        moment.setMediaType(dto.getMediaType());
+        moment.setVisibleRange(dto.getVisibleRange());
+        moment.setLatitude(dto.getLatitude());
+        moment.setLongitude(dto.getLongitude());
+        moment.setAddress(dto.getAddress());
+        moment.setCityCode(dto.getCityCode());
+
+        // 设置封面图
+        if (dto.getMediaType() == 1) {
+            // 图片类型,默认取第一张作为封面
+            if (dto.getImageUrls() != null && !dto.getImageUrls().isEmpty()) {
+                moment.setCoverUrl(dto.getImageUrls().get(0));
+            }
+        } else if (dto.getMediaType() == 2) {
+            // 视频类型,使用视频封面
+            moment.setCoverUrl(dto.getVideoCoverUrl());
+        }
+
+        // 6. 设置审核状态和发布时间
+        if (dto.getIsDraft() != null && dto.getIsDraft() == 1) {
+            // 保存草稿
+            moment.setAuditStatus(0); // 草稿
+            moment.setPublishTime(null);
+        } else {
+            // 立即发布,进入审核状态
+            moment.setAuditStatus(1); // 审核中
+            moment.setPublishTime(LocalDateTime.now());
+        }
+
+        // 7. 设置初始状态
+        moment.setStatus(1); // 正常
+        moment.setViewCount(0);
+        moment.setCreateTime(LocalDateTime.now());
+        moment.setUpdateTime(LocalDateTime.now());
+
+        // 8. 保存动态到数据库
+        int insertResult = momentMapper.insert(moment);
+        if (insertResult <= 0) {
+            throw new ServiceException("发布动态失败");
+        }
+
+        Long momentId = moment.getId();
+
+        // 9. 保存媒体信息到媒体表
+        if (dto.getMediaType() == 1) {
+            // 保存图片
+            if (dto.getImageUrls() != null && !dto.getImageUrls().isEmpty()) {
+                for (int i = 0; i < dto.getImageUrls().size(); i++) {
+                    MomentMedia media = new MomentMedia();
+                    media.setMomentId(momentId);
+                    media.setMediaUrl(dto.getImageUrls().get(i));
+                    media.setMediaType(1); // 图片
+                    media.setSortOrder(i + 1);
+                    media.setCreateTime(LocalDateTime.now());
+                    //获取文件的格式
+                    String fileFormat = dto.getImageUrls().get(i).substring(dto.getImageUrls().get(i).lastIndexOf(".") + 1);
+                    media.setFileFormat(fileFormat);
+                    mediaMapper.insert(media);
+                }
+            }
+        } else if (dto.getMediaType() == 2) {
+            // 保存视频
+            MomentMedia media = new MomentMedia();
+            media.setMomentId(momentId);
+            media.setMediaUrl(dto.getVideoUrl());
+            media.setMediaType(2); // 视频
+            media.setSortOrder(1);
+            media.setCreateTime(LocalDateTime.now());
+            mediaMapper.insert(media);
+        }
+        log.info("发布动态成功,动态ID:{},技师ID:{},是否草稿:{}", momentId, technician.getId(), dto.getIsDraft());
+        return momentId;
+    }
+
+    /**
+     * 查询草稿箱列表
+     *
+     * @param openId   技师OpenID
+     * @param pageNum  页码
+     * @param pageSize 每页数量
+     * @return List<MomentListVO> 草稿列表
+     */
+    @Override
+    public List<MomentListVO> getDraftMoments(String openId, Integer pageNum, Integer pageSize) {
+        if (openId == null || openId.trim().isEmpty()) {
+            throw new ServiceException("openId不能为空");
+        }
+
+        // 1. 根据openId查询技师信息
+        LambdaQueryWrapper<TJs> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(TJs::getcOpenId, openId);
+        TJs technician = tJsMapper.selectOne(queryWrapper);
+        if (technician == null) {
+            throw new ServiceException("技师信息不存在");
+        }
+
+        // 2. 查询该技师的草稿列表
+        Page<TechnicianMoment> page = new Page<>(pageNum, pageSize);
+        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);
+        List<TechnicianMoment> moments = momentPage.getRecords();
+        if (moments == null || moments.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        // 3. 组装VO
+        return moments.stream().map(moment -> {
+            MomentListVO vo = new MomentListVO();
+            BeanUtils.copyProperties(moment, vo);
+            vo.setTechnicianNickName(technician.getcNickName());
+            vo.setTechnicianAvatar(technician.getcPortrait());
+            vo.setTechnicianStatus(getTechnicianStatus(technician));
+            // 草稿的发布时间使用创建时间
+            vo.setPublishTime(moment.getCreateTime());
+            return vo;
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * 查询动态列表(管理后台)
+     *
+     * @param queryDTO 查询参数
+     * @return Page<MomentManageVO>分页结果
+     */
+    @Override
+    public Page<MomentManageVO> getMomentManageList(MomentManageQueryDTO queryDTO) {
+        // 1. 参数处理
+        Integer pageNum = queryDTO.getPageNum() != null ? queryDTO.getPageNum() : 1;
+        Integer pageSize = queryDTO.getPageSize() != null ? queryDTO.getPageSize() : 10;
+        String technicianName = queryDTO.getTechnicianName();
+        Integer auditStatus = queryDTO.getAuditStatus();
+        String startTime = queryDTO.getStartTime();
+        if(StringUtils.isNotBlank(startTime)){
+            startTime = startTime + " 00:00:00";
+        }
+        String endTime = queryDTO.getEndTime();
+        if(StringUtils.isNotBlank(endTime)){
+            endTime = endTime + " 23:59:59";
+        }
+
+        // 2. 构建查询条件
+        LambdaQueryWrapper<TechnicianMoment> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(TechnicianMoment::getStatus, 1); // 只查询正常状态的动态
+
+        // 如果传入了审核状态,则按照审核状态查询
+        if (auditStatus != null) {
+            queryWrapper.eq(TechnicianMoment::getAuditStatus, auditStatus);
+        } else {
+            // 默认查询全部的动态(待审核、审核通过、审核拒绝)
+            queryWrapper.in(TechnicianMoment::getAuditStatus, 1, 2, 3);
+        }
+
+        // 技师姓名查询
+        if (technicianName != null && !technicianName.trim().isEmpty()) {
+            //根据技师姓名查询技师的ID
+            LambdaQueryWrapper<TJs> jsQueryWrapper = new LambdaQueryWrapper<>();
+            jsQueryWrapper.like(TJs::getcNickName, technicianName);
+            TJs technician = tJsMapper.selectOne(jsQueryWrapper);
+            if (technician == null) {
+                throw new ServiceException("技师不存在");
+            }
+            queryWrapper.eq(TechnicianMoment::getTechnicianId, technician.getId());
+        }
+
+        // 时间范围查询
+        if (startTime != null && !startTime.trim().isEmpty()) {
+            queryWrapper.ge(TechnicianMoment::getPublishTime, startTime);
+        }
+        if (endTime != null && !endTime.trim().isEmpty()) {
+            queryWrapper.le(TechnicianMoment::getPublishTime, endTime);
+        }
+
+        // 按创建时间倒序排序
+        queryWrapper.orderByDesc(TechnicianMoment::getCreateTime);
+
+        // 3. 分页查询
+        Page<TechnicianMoment> page = new Page<>(pageNum, pageSize);
+        Page<TechnicianMoment> momentPage = momentMapper.selectPage(page, queryWrapper);
+
+        // 4. 组装VO
+        Page<MomentManageVO> voPage = new Page<>(pageNum, pageSize);
+        voPage.setTotal(momentPage.getTotal());
+        voPage.setCurrent(momentPage.getCurrent());
+        voPage.setSize(momentPage.getSize());
+
+        List<TechnicianMoment> moments = momentPage.getRecords();
+        if (moments == null || moments.isEmpty()) {
+            voPage.setRecords(new ArrayList<>());
+            return voPage;
+        }
+
+        // 5. 批量查询技师信息
+        List<String> technicianIds = moments.stream()
+                .map(TechnicianMoment::getTechnicianId)
+                .collect(Collectors.toList());
+        Map<String, TJs> technicianMap = getTechnicianMap(technicianIds);
+
+        // 6. 组装VO列表
+        List<MomentManageVO> voList = moments.stream().map(moment -> {
+            MomentManageVO vo = new MomentManageVO();
+            BeanUtils.copyProperties(moment, vo);
+
+            // 设置技师信息
+            TJs technician = technicianMap.get(moment.getTechnicianId());
+            if (technician != null) {
+                // 设置技师姓名
+                vo.setTechnicianName(technician.getcName());
+                // 设置技师昵称
+                vo.setTechnicianNickname(technician.getcNickName());
+            }
+            return vo;
+        }).collect(Collectors.toList());
+        voPage.setRecords(voList);
+        log.info("查询动态管理列表,页码:{},每页数量:{},总记录数:{}", pageNum, pageSize, voPage.getTotal());
+        return voPage;
+    }
+
+    /**
+     * 审核动态(通过/拒绝)
+     *
+     * @param momentId     动态ID
+     * @param auditStatus  审核状态:2-通过,3-拒绝
+     * @param rejectReason 拒绝原因(拒绝时必填)
+     * @return 是否成功
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean auditMoment(MomentAuditDTO dto) {
+        // 1. 参数校验
+        if (dto.getMomentId() == null) {
+            throw new ServiceException("动态ID不能为空");
+        }
+        if (dto.getAuditStatus() == null || (dto.getAuditStatus() != 2 && dto.getAuditStatus() != 3)) {
+            throw new ServiceException("审核状态不正确");
+        }
+        if (dto.getAuditStatus() == 3 && (dto.getRejectReason() == null || dto.getRejectReason().trim().isEmpty())) {
+            throw new ServiceException("拒绝时必须填写拒绝原因");
+        }
+
+        // 2. 查询动态信息
+        TechnicianMoment moment = momentMapper.selectById(dto.getMomentId());
+        if (moment == null) {
+            throw new ServiceException("动态不存在");
+        }
+
+        // 3. 校验动态状态
+        if (moment.getStatus() != 1) {
+            throw new ServiceException("动态已删除,无法审核");
+        }
+        if (moment.getAuditStatus() != 1) {
+            throw new ServiceException("只能审核待审核状态的动态");
+        }
+
+        // 4. 更新审核状态
+        moment.setAuditStatus(dto.getAuditStatus());
+        moment.setRejectReason(dto.getRejectReason());
+        moment.setUpdateTime(LocalDateTime.now());
+
+        int updateResult = momentMapper.updateById(moment);
+        if (updateResult <= 0) {
+            throw new ServiceException("审核失败");
+        }
+        log.info("审核动态成功,动态ID:{},审核结果:{},拒绝原因:{}", dto.getMomentId(), dto.getAuditStatus(), dto.getRejectReason());
+        return true;
+    }
+
+    /**
+     * 根据动态ID查询动态简要详情(包含媒体URL列表)
+     *
+     * @param momentId 动态ID
+     * @return MomentSimpleDetailVO动态简要详情
+     */
+    @Override
+    public MomentSimpleDetailVO getMomentSimpleDetail(Long momentId) {
+        // 1. 参数校验
+        if (momentId == null) {
+            throw new ServiceException("动态ID不能为空");
+        }
+
+        // 2. 查询动态信息
+        Map<String, Object> result = momentMapper.selectMomentSimpleDetail(momentId);
+        if (result == null || result.isEmpty()) {
+            throw new ServiceException("动态不存在");
+        }
+
+        // 3. 组装VO
+        MomentSimpleDetailVO vo = new MomentSimpleDetailVO();
+        vo.setId(((Number) result.get("id")).longValue());
+        vo.setTitle((String) result.get("title"));
+        vo.setContent((String) result.get("content"));
+        vo.setMediaType((Integer) result.get("media_type"));
+
+        // 4. 处理媒体URL列表
+        String mediaUrlsStr = (String) result.get("media_urls");
+        if (mediaUrlsStr != null && !mediaUrlsStr.trim().isEmpty()) {
+            // 将逗号分隔的字符串转换为List
+            List<String> mediaUrls = Arrays.asList(mediaUrlsStr.split(","));
+            vo.setMediaUrls(mediaUrls);
+        } else {
+            vo.setMediaUrls(new ArrayList<>());
+        }
+        log.info("查询动态简要详情成功,动态ID:{}", momentId);
+        return vo;
+    }
 }

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

@@ -87,7 +87,24 @@
         SET
             view_count = view_count + 1
         WHERE
-            id = #{momentId}
+            id = #{momentId} and status = 1 and audit_status = 2
     </update>
 
+    <!-- 根据动态ID查询动态简要详情(包含媒体URL列表) -->
+    <select id="selectMomentSimpleDetail" resultType="java.util.HashMap">
+        SELECT
+            a.id,
+            a.title,
+            a.content,
+            a.media_type,
+            GROUP_CONCAT(b.media_url ORDER BY b.sort_order SEPARATOR ',') as media_urls
+        FROM
+            t_technician_moment a
+            LEFT JOIN t_moment_media b ON a.id = b.moment_id
+        WHERE
+            a.id = #{momentId}
+        GROUP BY
+            a.id, a.title, a.content, a.media_type
+    </select>
+
 </mapper>