feat(AI资源广场): Skill资源相关功能开发

资源评论基于Gitlink评论通用化改造
This commit is contained in:
欧涛 2026-04-30 16:39:07 +08:00
parent 3a2560f700
commit 356ffd8915
15 changed files with 1305 additions and 1 deletions

View File

@ -16,6 +16,9 @@ import com.microservices.zone.dataset.service.IZoneDataSetService;
import com.microservices.zone.detail.domain.vo.ZoneBaseDataVo;
import com.microservices.zone.detail.domain.vo.ZoneDetailDataVo;
import com.microservices.zone.detail.service.IZoneDetailService;
import com.microservices.zone.journals.domain.ZoneJournals;
import com.microservices.zone.journals.domain.vo.ZoneJournalsParentDataVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsSearchVo;
import com.microservices.zone.member.domain.vo.ZoneMemberDataVo;
import com.microservices.zone.member.domain.vo.ZoneMemberOpenSearchVo;
import com.microservices.zone.member.domain.vo.ZoneMemberOverviewVo;
@ -242,7 +245,7 @@ public class OpenController extends BaseController {
@GetMapping("/{zoneId}/member/homePageList")
@ApiOperation("获取首页特色专区会员列表")
public GenericsTableDataInfo<ZoneMemberDataVo> homePageMemberList(@ApiParam(name = "zoneId", value = "专区Id", required = true) @PathVariable("zoneId") Long zoneId) {
List<ZoneMemberDataVo> list = zoneMemberService.selectZoneMemberHomePageMemberList(zoneId,true);
List<ZoneMemberDataVo> list = zoneMemberService.selectZoneMemberHomePageMemberList(zoneId, true);
return getGenericsDataTable(list);
}
@ -452,4 +455,27 @@ public class OpenController extends BaseController {
startPage();
return zoneResourceService.selectAiSkillResourceList(zoneResource);
}
// ==================== 通用评论 - 开放接口 ====================
@Autowired
private com.microservices.zone.journals.service.IZoneJournalsService zoneJournalsService;
/**
* 公开-资源评论列表
*/
@GetMapping("/ai-square/{resourceId}/journals")
@ApiOperation("公开-资源评论列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "pageNum", value = "当前记录起始索引", paramType = "query", dataType = "Integer"),
@ApiImplicitParam(name = "pageSize", value = "每页显示记录数", paramType = "query", dataType = "Integer")
})
public GenericsTableDataInfo<ZoneJournalsParentDataVo> journalsList(
@ApiParam(name = "resourceId", value = "资源ID", required = true) @PathVariable("resourceId") Long resourceId,
ZoneJournalsSearchVo searchVo) {
searchVo.setJournalized_id(resourceId);
searchVo.setJournalized_type(ZoneJournals.JOURNAL_TYPE_AI_SKILL);
startPage();
return new GenericsTableDataInfo<>(zoneJournalsService.selectJournalsList(searchVo));
}
}

View File

@ -0,0 +1,108 @@
package com.microservices.zone.journals.controller;
import com.microservices.common.core.web.controller.BaseController;
import com.microservices.common.core.web.domain.AjaxResult;
import com.microservices.common.core.web.page.TableDataInfo;
import com.microservices.common.log.annotation.Log;
import com.microservices.common.log.enums.BusinessType;
import com.microservices.common.security.annotation.RequiresPermissions;
import com.microservices.zone.journals.domain.ZoneJournals;
import com.microservices.zone.journals.domain.vo.ZoneJournalsInputVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsParentDataVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsSearchVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsUpdateVo;
import com.microservices.zone.journals.service.IZoneJournalsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 通用评论Controller
*
* @author otto
* @date 2025-04-30
*/
@RestController
@RequestMapping("/zone/journals")
@Api(tags = "通用评论接口")
public class ZoneJournalsController extends BaseController {
@Autowired
private IZoneJournalsService zoneJournalsService;
/**
* 查询评论列表二级结构
*/
@RequiresPermissions("zone:journals:list")
@GetMapping("/list")
@ApiOperation("查询评论列表")
public TableDataInfo<ZoneJournalsParentDataVo> list(ZoneJournalsSearchVo searchVo) {
startPage();
List<ZoneJournalsParentDataVo> list = zoneJournalsService.selectJournalsList(searchVo);
return getDataTable(list);
}
/**
* 获取评论详细信息
*/
@RequiresPermissions("zone:journals:query")
@GetMapping("/{id}")
@ApiOperation("获取评论详细信息")
public AjaxResult getInfo(@ApiParam(value = "评论ID", required = true) @PathVariable("id") Long id) {
ZoneJournals zoneJournals = zoneJournalsService.selectZoneJournalsById(id);
return success(zoneJournals);
}
/**
* 新增评论
*/
@RequiresPermissions("zone:journals:add")
@Log(title = "新增评论", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增评论")
public AjaxResult add(@RequestBody @Validated ZoneJournalsInputVo inputVo) {
Long journalId = zoneJournalsService.insertJournals(inputVo);
return success(journalId);
}
/**
* 修改评论
*/
@RequiresPermissions("zone:journals:edit")
@Log(title = "修改评论", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改评论")
public AjaxResult edit(@RequestBody @Validated ZoneJournalsUpdateVo updateVo) {
int result = zoneJournalsService.updateJournals(updateVo);
return toAjax(result);
}
/**
* 删除评论
*/
@RequiresPermissions("zone:journals:remove")
@Log(title = "删除评论", businessType = BusinessType.DELETE)
@DeleteMapping("/{id}")
@ApiOperation("删除评论")
public AjaxResult remove(@ApiParam(value = "评论ID", required = true) @PathVariable("id") Long id) {
int result = zoneJournalsService.deleteJournalsById(id);
return toAjax(result);
}
/**
* 批量删除评论
*/
@RequiresPermissions("zone:journals:remove")
@Log(title = "批量删除评论", businessType = BusinessType.DELETE)
@DeleteMapping("/batch/{ids}")
@ApiOperation("批量删除评论")
public AjaxResult removeBatch(@ApiParam(value = "评论ID数组", required = true) @PathVariable Long[] ids) {
int result = zoneJournalsService.deleteJournalsByIds(ids);
return toAjax(result);
}
}

View File

@ -0,0 +1,91 @@
package com.microservices.zone.journals.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.microservices.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* 通用评论对象 zone_journals
*
* @author otto
* @date 2025-04-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("通用评论对象")
public class ZoneJournals extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 评论对象类型常量
*/
public static final String JOURNAL_TYPE_AI_SKILL = "aiSkill";
public static final String JOURNAL_TYPE_AI_MODEL = "aiModel";
public static final String JOURNAL_TYPE_AI_DATASET = "aiDataset";
public static final String JOURNAL_TYPE_ZONE_RESOURCE = "zoneResource";
/** 评论ID */
@ApiModelProperty(value = "评论ID")
private Long id;
/** 评论对象ID */
@ApiModelProperty(value = "评论对象ID")
private Long journalizedId;
/** 评论对象类型 */
@ApiModelProperty(value = "评论对象类型")
private String journalizedType;
/** GitLink用户ID */
@ApiModelProperty(value = "GitLink用户ID")
private Long userId;
/** 评论内容 */
@ApiModelProperty(value = "评论内容")
private String notes;
/** 父级评论ID */
@ApiModelProperty(value = "父级评论ID")
private Long parentId;
/** 回复的评论ID */
@ApiModelProperty(value = "回复的评论ID")
private Long replyId;
/** 评论数量 */
@ApiModelProperty(value = "评论数量")
private Integer commentsCount;
/** 附件标识列表 */
@ApiModelProperty(value = "附件标识列表")
private String attachmentIdentifiers;
/** @用户列表 */
@ApiModelProperty(value = "@用户列表")
private String receiversLogin;
/** 是否隐私评论 */
@ApiModelProperty(value = "是否隐私评论")
private String privateNotes;
/** 删除标志 */
@ApiModelProperty(value = "删除标志")
private String delFlag;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建时间")
private Date createdOn;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "更新时间")
private Date updatedOn;
}

View File

@ -0,0 +1,23 @@
package com.microservices.zone.journals.domain.vo;
import com.microservices.system.api.domain.SimpleSysUser;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 通用评论子级评论数据VO
*
* @author otto
* @date 2025-04-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("子级评论数据对象")
public class ZoneJournalsChildrenDataVo extends ZoneJournalsDataVo {
/** 回复的用户 */
@ApiModelProperty(value = "回复的用户")
private SimpleSysUser reply_user;
}

View File

@ -0,0 +1,56 @@
package com.microservices.zone.journals.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.microservices.system.api.domain.SysFileInfo;
import com.microservices.system.api.domain.SimpleSysUser;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* 通用评论基础数据VO
*
* @author otto
* @date 2025-04-30
*/
@Data
@ApiModel("评论数据对象")
public class ZoneJournalsDataVo {
/** 评论ID */
@ApiModelProperty(value = "评论ID")
private Long id;
/** 评论用户 */
@ApiModelProperty(value = "评论用户")
private SimpleSysUser user;
/** 评论内容 */
@ApiModelProperty(value = "评论内容")
private String notes;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建时间")
private Date created_on;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "更新时间")
private Date updated_on;
/** 附件列表 */
@ApiModelProperty(value = "附件列表")
private List<SysFileInfo> attachments;
/** 评论数量 */
@ApiModelProperty(value = "评论数量")
private Integer comments_count = 0;
/** 是否详情 */
@ApiModelProperty(value = "是否详情")
private Boolean is_journal_detail = false;
}

View File

@ -0,0 +1,56 @@
package com.microservices.zone.journals.domain.vo;
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;
/**
* 通用评论输入VO
*
* @author otto
* @date 2025-04-30
*/
@Data
@ApiModel("评论输入对象")
public class ZoneJournalsInputVo {
/** 评论对象ID */
@NotNull(message = "评论对象ID不能为空")
@ApiModelProperty(value = "评论对象ID", required = true)
private Long journalized_id;
/** 评论对象类型 */
@NotBlank(message = "评论对象类型不能为空")
@ApiModelProperty(value = "评论对象类型", required = true)
private String journalized_type;
/** 父级评论ID */
@ApiModelProperty(value = "父级评论ID")
private Long parent_id;
/** 回复的评论ID */
@ApiModelProperty(value = "回复的评论ID")
private Long reply_id;
/** 评论内容 */
@NotBlank(message = "评论内容不能为空")
@Size(max = 2000, message = "评论内容不能超过2000个字符")
@ApiModelProperty(value = "评论内容", required = true)
private String notes;
/** @用户列表 */
@ApiModelProperty(value = "@用户列表")
private String[] receivers_login;
/** 附件标识列表 */
@ApiModelProperty(value = "附件标识列表")
private String attachment_identifiers;
/** 是否隐私评论 */
@ApiModelProperty(value = "是否隐私评论")
private String private_notes;
}

View File

@ -0,0 +1,24 @@
package com.microservices.zone.journals.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 通用评论父级评论数据VO
*
* @author otto
* @date 2025-04-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("父级评论数据对象")
public class ZoneJournalsParentDataVo extends ZoneJournalsDataVo {
/** 子级评论列表 */
@ApiModelProperty(value = "子级评论列表")
private List<ZoneJournalsChildrenDataVo> children_journals;
}

View File

@ -0,0 +1,32 @@
package com.microservices.zone.journals.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 通用评论查询VO
*
* @author otto
* @date 2025-04-30
*/
@Data
@ApiModel("评论查询对象")
public class ZoneJournalsSearchVo {
/** 评论对象ID */
@ApiModelProperty(value = "评论对象ID")
private Long journalized_id;
/** 评论对象类型 */
@ApiModelProperty(value = "评论对象类型")
private String journalized_type;
/** 评论用户ID */
@ApiModelProperty(value = "评论用户ID")
private Long user_id;
/** 是否隐私评论 */
@ApiModelProperty(value = "是否隐私评论")
private String private_notes;
}

View File

@ -0,0 +1,39 @@
package com.microservices.zone.journals.domain.vo;
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;
/**
* 通用评论更新VO
*
* @author otto
* @date 2025-04-30
*/
@Data
@ApiModel("评论更新对象")
public class ZoneJournalsUpdateVo {
/** 评论ID */
@NotNull(message = "评论ID不能为空")
@ApiModelProperty(value = "评论ID", required = true)
private Long id;
/** 评论内容 */
@NotBlank(message = "评论内容不能为空")
@Size(max = 2000, message = "评论内容不能超过2000个字符")
@ApiModelProperty(value = "评论内容", required = true)
private String notes;
/** @用户列表 */
@ApiModelProperty(value = "@用户列表")
private String[] receivers_login;
/** 附件标识列表 */
@ApiModelProperty(value = "附件标识列表")
private String attachment_identifiers;
}

View File

@ -0,0 +1,115 @@
package com.microservices.zone.journals.mapper;
import com.microservices.zone.journals.domain.ZoneJournals;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 通用评论Mapper接口
*
* @author otto
* @date 2025-04-30
*/
@Mapper
public interface ZoneJournalsMapper {
/**
* 查询评论
*
* @param id 评论主键
* @return 评论
*/
ZoneJournals selectZoneJournalsById(Long id);
/**
* 查询评论列表父评论
*
* @param zoneJournals 评论查询条件
* @return 评论集合
*/
List<ZoneJournals> selectZoneJournalsList(ZoneJournals zoneJournals);
/**
* 查询子评论列表
*
* @param parentId 父评论ID
* @return 子评论集合
*/
List<ZoneJournals> selectChildrenZoneJournalsList(@Param("parentId") Long parentId);
/**
* 新增评论
*
* @param zoneJournals 评论
* @return 结果
*/
int insertZoneJournals(ZoneJournals zoneJournals);
/**
* 修改评论
*
* @param zoneJournals 评论
* @return 结果
*/
int updateZoneJournals(ZoneJournals zoneJournals);
/**
* 删除评论逻辑删除
*
* @param id 评论主键
* @return 结果
*/
int deleteZoneJournalsById(Long id);
/**
* 批量删除评论逻辑删除
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
int deleteZoneJournalsByIds(Long[] ids);
/**
* 删除子评论逻辑删除
*
* @param parentId 父评论ID
* @return 结果
*/
int deleteZoneJournalsByParentId(@Param("parentId") Long parentId);
/**
* 根据类型和评论对象Id删除所有评论
*
* @param type 类型
* @param journalId 评论对象Id
*/
void deleteZoneJournalsByTypeAndJournalId(@Param("type") String type, @Param("journalId") Long journalId);
/**
* 批量更新关联ID
*
* @param type 类型
* @param oldJournalId 旧评论对象Id
* @param journalId 新评论对象Id
*/
void updateJournalsIdByTypeAndOldId(@Param("type") String type, @Param("oldJournalId") Long oldJournalId, @Param("journalId") Long journalId);
/**
* 更新父评论的子评论数量
*
* @param id 父评论ID
* @return 结果
*/
int updateCommentsCountById(@Param("id") Long id);
/**
* 统计评论数量
*
* @param journalizedId 评论对象ID
* @param journalizedType 评论对象类型
* @return 评论数量
*/
int countByJournalized(@Param("journalizedId") Long journalizedId, @Param("journalizedType") String journalizedType);
}

View File

@ -0,0 +1,83 @@
package com.microservices.zone.journals.service;
import com.microservices.zone.journals.domain.ZoneJournals;
import com.microservices.zone.journals.domain.vo.ZoneJournalsInputVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsParentDataVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsSearchVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsUpdateVo;
import java.util.List;
/**
* 通用评论Service接口
*
* @author otto
* @date 2025-04-30
*/
public interface IZoneJournalsService {
/**
* 查询评论
*
* @param id 评论主键
* @return 评论
*/
ZoneJournals selectZoneJournalsById(Long id);
/**
* 查询评论列表二级结构
*
* @param searchVo 查询条件
* @return 评论列表父评论包含子评论
*/
List<ZoneJournalsParentDataVo> selectJournalsList(ZoneJournalsSearchVo searchVo);
/**
* 新增评论
*
* @param inputVo 评论输入信息
* @return 评论ID
*/
Long insertJournals(ZoneJournalsInputVo inputVo);
/**
* 修改评论
*
* @param updateVo 评论更新信息
* @return 结果
*/
int updateJournals(ZoneJournalsUpdateVo updateVo);
/**
* 删除评论级联删除子评论
*
* @param id 评论主键
* @return 结果
*/
int deleteJournalsById(Long id);
/**
* 批量删除评论
*
* @param ids 需要删除的评论主键集合
* @return 结果
*/
int deleteJournalsByIds(Long[] ids);
/**
* 根据类型和评论对象Id删除所有评论
*
* @param type 类型
* @param journalId 评论对象Id
*/
void deleteJournalsByTypeAndJournalId(String type, Long journalId);
/**
* 批量更新关联ID
*
* @param type 类型
* @param oldJournalId 旧评论对象Id
* @param journalId 新评论对象Id
*/
void updateJournalsIdByTypeAndOldId(String type, Long oldJournalId, Long journalId);
}

View File

@ -0,0 +1,410 @@
package com.microservices.zone.journals.service.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.RemoteFileService;
import com.microservices.system.api.domain.SysFileInfo;
import com.microservices.system.api.domain.SimpleSysUser;
import com.microservices.system.api.utils.FeignUtils;
import com.microservices.zone.common.service.IZoneCommonService;
import com.microservices.zone.journals.domain.ZoneJournals;
import com.microservices.zone.journals.domain.vo.ZoneJournalsChildrenDataVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsInputVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsParentDataVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsSearchVo;
import com.microservices.zone.journals.domain.vo.ZoneJournalsUpdateVo;
import com.microservices.zone.journals.mapper.ZoneJournalsMapper;
import com.microservices.zone.journals.service.IZoneJournalsService;
import com.microservices.zone.resource.service.IZoneResourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 通用评论Service业务层处理
*
* @author otto
* @date 2025-04-30
*/
@Service
public class ZoneJournalsServiceImpl implements IZoneJournalsService {
@Autowired
private ZoneJournalsMapper zoneJournalsMapper;
@Autowired
private IZoneCommonService zoneCommonService;
@Autowired
private IZoneResourceService zoneResourceService;
@Autowired
private RemoteFileService remoteFileService;
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* 查询评论
*
* @param id 评论主键
* @return 评论
*/
@Override
public ZoneJournals selectZoneJournalsById(Long id) {
ZoneJournals zoneJournals = zoneJournalsMapper.selectZoneJournalsById(id);
if (zoneJournals == null) {
throw new ServiceException("该评论不存在(评论Id[%d])", id);
}
// 校验评论对象是否存在
checkByType(zoneJournals);
return zoneJournals;
}
/**
* 查询评论列表二级结构
*
* @param searchVo 查询条件
* @return 评论列表父评论包含子评论
*/
@Override
public List<ZoneJournalsParentDataVo> selectJournalsList(ZoneJournalsSearchVo searchVo) {
// 校验评论对象是否存在
ZoneJournals checkJournal = new ZoneJournals();
checkJournal.setJournalizedId(searchVo.getJournalized_id());
checkJournal.setJournalizedType(searchVo.getJournalized_type());
checkByType(checkJournal);
// 查询父评论列表
ZoneJournals query = new ZoneJournals();
query.setJournalizedId(searchVo.getJournalized_id());
query.setJournalizedType(searchVo.getJournalized_type());
query.setUserId(searchVo.getUser_id());
query.setPrivateNotes(searchVo.getPrivate_notes());
List<ZoneJournals> parentJournalsList = zoneJournalsMapper.selectZoneJournalsList(query);
// 转换为VO并组装子评论
return parentJournalsList.stream()
.map(this::toParentDataVo)
.collect(Collectors.toList());
}
/**
* 新增评论
*
* @param inputVo 评论输入信息
* @return 评论ID
*/
@Override
public Long insertJournals(ZoneJournalsInputVo inputVo) {
// 校验评论对象类型
ZoneJournals zoneJournals = new ZoneJournals();
zoneJournals.setJournalizedId(inputVo.getJournalized_id());
zoneJournals.setJournalizedType(inputVo.getJournalized_type());
checkByType(zoneJournals);
// 如果是子评论校验父评论
if (inputVo.getParent_id() != null) {
ZoneJournals parentJournal = selectZoneJournalsById(inputVo.getParent_id());
if (parentJournal.getParent_id() != null) {
throw new ServiceException("该评论非父级评论(评论ID[%d])", parentJournal.getId());
}
if (!parentJournal.getJournalizedType().equals(inputVo.getJournalized_type())) {
throw new ServiceException("当前父级评论与当前评论类型不相同");
}
if (!parentJournal.getJournalizedId().equals(inputVo.getJournalized_id())) {
throw new ServiceException("当前父级评论与当前评论对象不相同");
}
}
// 如果有回复评论校验回复评论
if (inputVo.getReply_id() != null) {
if (inputVo.getParent_id() == null) {
throw new ServiceException("一级评论不能进行回复操作");
}
ZoneJournals replyJournal = selectZoneJournalsById(inputVo.getReply_id());
if (!replyJournal.getJournalizedType().equals(inputVo.getJournalized_type())) {
throw new ServiceException("当前回复评论与当前评论类型不相同");
}
if (!replyJournal.getJournalizedId().equals(inputVo.getJournalized_id())) {
throw new ServiceException("当前回复评论与当前评论对象不相同");
}
}
// 校验附件列表
checkAttachments(inputVo.getAttachment_identifiers());
// 构建评论对象
ZoneJournals newJournal = new ZoneJournals();
newJournal.setJournalizedId(inputVo.getJournalized_id());
newJournal.setJournalizedType(inputVo.getJournalized_type());
newJournal.setParentId(inputVo.getParent_id());
newJournal.setReplyId(inputVo.getReply_id());
newJournal.setNotes(inputVo.getNotes());
newJournal.setPrivateNotes(StringUtils.isEmpty(inputVo.getPrivate_notes()) ? "0" : inputVo.getPrivate_notes());
newJournal.setAttachmentIdentifiers(inputVo.getAttachment_identifiers());
// 处理@用户列表
if (inputVo.getReceivers_login() != null && inputVo.getReceivers_login().length > 0) {
try {
newJournal.setReceiversLogin(objectMapper.writeValueAsString(inputVo.getReceivers_login()));
} catch (Exception e) {
throw new ServiceException("@用户列表格式错误");
}
}
// 获取当前用户ID
Long userId = SecurityUtils.getUserId();
if (userId == null || userId == 0) {
throw new ServiceException("无法获取当前用户信息");
}
newJournal.setUserId(userId);
newJournal.setCreateBy(SecurityUtils.getUsername());
newJournal.setCreatedOn(DateUtils.getNowDate());
// 插入评论
zoneJournalsMapper.insertZoneJournals(newJournal);
// 如果是子评论更新父评论的子评论数量
if (inputVo.getParent_id() != null) {
zoneJournalsMapper.updateCommentsCountById(inputVo.getParent_id());
}
return newJournal.getId();
}
/**
* 修改评论
*
* @param updateVo 评论更新信息
* @return 结果
*/
@Override
public int updateJournals(ZoneJournalsUpdateVo updateVo) {
// 查询原评论并校验
ZoneJournals oldJournal = selectZoneJournalsById(updateVo.getId());
// 校验是否为评论创建者
Long userId = SecurityUtils.getUserId();
if (!oldJournal.getUserId().equals(userId)) {
throw new ServiceException("只能修改自己的评论");
}
// 校验附件列表
checkAttachments(updateVo.getAttachment_identifiers());
// 构建更新对象
ZoneJournals updateJournal = new ZoneJournals();
updateJournal.setId(updateVo.getId());
updateJournal.setNotes(updateVo.getNotes());
updateJournal.setAttachmentIdentifiers(updateVo.getAttachment_identifiers());
// 处理@用户列表
if (updateVo.getReceivers_login() != null && updateVo.getReceivers_login().length > 0) {
try {
updateJournal.setReceiversLogin(objectMapper.writeValueAsString(updateVo.getReceivers_login()));
} catch (Exception e) {
throw new ServiceException("@用户列表格式错误");
}
}
updateJournal.setUpdateBy(SecurityUtils.getUsername());
updateJournal.setUpdatedOn(DateUtils.getNowDate());
return zoneJournalsMapper.updateZoneJournals(updateJournal);
}
/**
* 删除评论级联删除子评论
*
* @param id 评论主键
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteJournalsById(Long id) {
ZoneJournals zoneJournals = selectZoneJournalsById(id);
// 校验是否为评论创建者
Long userId = SecurityUtils.getUserId();
if (!zoneJournals.getUserId().equals(userId)) {
throw new ServiceException("只能删除自己的评论");
}
// 如果是父级评论先删除所有子评论
if (zoneJournals.getParentId() == null) {
List<ZoneJournals> childrenJournalsList = zoneJournalsMapper.selectChildrenZoneJournalsList(zoneJournals.getId());
for (ZoneJournals childrenJournal : childrenJournalsList) {
zoneJournalsMapper.deleteZoneJournalsById(childrenJournal.getId());
}
}
return zoneJournalsMapper.deleteZoneJournalsById(id);
}
/**
* 批量删除评论
*
* @param ids 需要删除的评论主键集合
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteJournalsByIds(Long[] ids) {
int success = 0;
for (Long id : ids) {
success += deleteJournalsById(id);
}
return success;
}
/**
* 根据类型和评论对象Id删除所有评论
*
* @param type 类型
* @param journalId 评论对象Id
*/
@Override
public void deleteJournalsByTypeAndJournalId(String type, Long journalId) {
zoneJournalsMapper.deleteZoneJournalsByTypeAndJournalId(type, journalId);
}
/**
* 批量更新关联ID
*
* @param type 类型
* @param oldJournalId 旧评论对象Id
* @param journalId 新评论对象Id
*/
@Override
public void updateJournalsIdByTypeAndOldId(String type, Long oldJournalId, Long journalId) {
zoneJournalsMapper.updateJournalsIdByTypeAndOldId(type, oldJournalId, journalId);
}
/**
* 根据类型校验评论对象是否存在
*
* @param zoneJournals 评论对象
*/
private void checkByType(ZoneJournals zoneJournals) {
switch (zoneJournals.getJournalizedType()) {
case ZoneJournals.JOURNAL_TYPE_AI_SKILL:
case ZoneJournals.JOURNAL_TYPE_AI_MODEL:
case ZoneJournals.JOURNAL_TYPE_AI_DATASET:
case ZoneJournals.JOURNAL_TYPE_ZONE_RESOURCE:
// 校验资源是否存在
zoneResourceService.selectZoneResourceById(zoneJournals.getJournalizedId());
break;
default:
throw new ServiceException("当前不支持该类型评论(评论类型[%s])", zoneJournals.getJournalizedType());
}
}
/**
* 校验附件列表
*
* @param attachmentIdentifiers 附件标识列表
*/
private void checkAttachments(String attachmentIdentifiers) {
if (StringUtils.isEmpty(attachmentIdentifiers)) {
return;
}
try {
List<SysFileInfo> fileInfos = remoteFileService.getFileByIdentifiers(attachmentIdentifiers);
if (CollectionUtils.isEmpty(fileInfos)) {
throw new ServiceException("附件不存在或已被删除");
}
} catch (Exception e) {
throw new ServiceException("附件校验失败:%s", e.getMessage());
}
}
/**
* 转换为父评论VO
*
* @param zoneJournals 评论实体
* @return 父评论VO
*/
private ZoneJournalsParentDataVo toParentDataVo(ZoneJournals zoneJournals) {
ZoneJournalsParentDataVo parentVo = new ZoneJournalsParentDataVo();
parentVo.setId(zoneJournals.getId());
parentVo.setNotes(zoneJournals.getNotes());
parentVo.setCreated_on(zoneJournals.getCreatedOn());
parentVo.setUpdated_on(zoneJournals.getUpdatedOn());
// 获取用户信息
SimpleSysUser user = zoneCommonService.getSimpleSysUserByUserId(zoneJournals.getUserId());
parentVo.setUser(user);
// 获取附件列表
parentVo.setAttachments(getAttachments(zoneJournals.getAttachmentIdentifiers()));
// 获取子评论列表
List<ZoneJournals> childrenJournalsList = zoneJournalsMapper.selectChildrenZoneJournalsList(zoneJournals.getId());
List<ZoneJournalsChildrenDataVo> childrenVoList = childrenJournalsList.stream()
.map(this::toChildrenDataVo)
.collect(Collectors.toList());
parentVo.setChildren_journals(childrenVoList);
parentVo.setComments_count(childrenVoList.size());
return parentVo;
}
/**
* 转换为子评论VO
*
* @param zoneJournals 评论实体
* @return 子评论VO
*/
private ZoneJournalsChildrenDataVo toChildrenDataVo(ZoneJournals zoneJournals) {
ZoneJournalsChildrenDataVo childrenVo = new ZoneJournalsChildrenDataVo();
childrenVo.setId(zoneJournals.getId());
childrenVo.setNotes(zoneJournals.getNotes());
childrenVo.setCreated_on(zoneJournals.getCreatedOn());
childrenVo.setUpdated_on(zoneJournals.getUpdatedOn());
// 获取用户信息
SimpleSysUser user = zoneCommonService.getSimpleSysUserByUserId(zoneJournals.getUserId());
childrenVo.setUser(user);
// 获取附件列表
childrenVo.setAttachments(getAttachments(zoneJournals.getAttachmentIdentifiers()));
// 获取回复用户信息
if (zoneJournals.getReplyId() != null) {
ZoneJournals replyJournal = zoneJournalsMapper.selectZoneJournalsById(zoneJournals.getReplyId());
if (replyJournal != null) {
SimpleSysUser replyUser = zoneCommonService.getSimpleSysUserByUserId(replyJournal.getUserId());
childrenVo.setReply_user(replyUser);
}
}
return childrenVo;
}
/**
* 获取附件列表
*
* @param attachmentIdentifiers 附件标识列表
* @return 附件列表
*/
private List<SysFileInfo> getAttachments(String attachmentIdentifiers) {
if (StringUtils.isEmpty(attachmentIdentifiers)) {
return new ArrayList<>();
}
try {
List<SysFileInfo> fileInfos = remoteFileService.getFileByIdentifiers(attachmentIdentifiers);
return CollectionUtils.isEmpty(fileInfos) ? new ArrayList<>() : fileInfos;
} catch (Exception e) {
return new ArrayList<>();
}
}
}

View File

@ -0,0 +1,47 @@
-- AI资源评论数据迁移脚本
-- 创建时间2025-04-30
-- 说明:将 ai_resource_comment 数据迁移到 zone_journals
-- 迁移数据
INSERT INTO zone_journals (
journalized_id,
journalized_type,
user_id,
notes,
parent_id,
reply_id,
attachment_identifiers,
private_notes,
del_flag,
create_by,
created_on,
update_by,
updated_on
)
SELECT
resource_id AS journalized_id,
'aiSkill' AS journalized_type,
user_id,
content AS notes,
parent_id,
reply_id,
attachment_identifiers,
'0' AS private_notes,
del_flag,
create_by,
create_time AS created_on,
update_by,
update_time AS updated_on
FROM ai_resource_comment
WHERE del_flag = '0';
-- 更新 comments_count统计每个父评论的子评论数量
UPDATE zone_journals zj
SET comments_count = (
SELECT COUNT(*)
FROM zone_journals zj2
WHERE zj2.parent_id = zj.id
AND zj2.del_flag = '0'
)
WHERE zj.parent_id IS NULL
AND zj.del_flag = '0';

View File

@ -0,0 +1,27 @@
-- Zone通用评论系统 v1 DDL 脚本
-- 创建时间2025-04-30
-- 说明:将 ai_resource_comment 改造为通用评论系统 zone_journals与 pms_journals 保持接口一致
-- 1. 创建通用评论表
CREATE TABLE `zone_journals` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '评论ID',
`journalized_id` BIGINT NOT NULL COMMENT '评论对象ID',
`journalized_type` VARCHAR(50) NOT NULL COMMENT '评论对象类型aiSkill/aiModel/aiDataset/zoneResource',
`user_id` BIGINT NOT NULL COMMENT 'GitLink用户ID',
`notes` TEXT NOT NULL COMMENT '评论内容',
`parent_id` BIGINT DEFAULT NULL COMMENT '父级评论IDNULL=顶级评论)',
`reply_id` BIGINT DEFAULT NULL COMMENT '回复的评论ID',
`comments_count` INT DEFAULT 0 COMMENT '评论数量',
`attachment_identifiers` TEXT DEFAULT NULL COMMENT '附件标识列表JSON',
`receivers_login` TEXT DEFAULT NULL COMMENT '@用户列表JSON数组',
`private_notes` VARCHAR(1) DEFAULT '0' COMMENT '是否隐私评论0-否1-是',
`del_flag` VARCHAR(1) DEFAULT '0' COMMENT '删除标志0-正常2-删除',
`create_by` VARCHAR(64) DEFAULT NULL COMMENT '创建者',
`created_on` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新者',
`updated_on` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_journalized` (`journalized_id`, `journalized_type`),
KEY `idx_parent_id` (`parent_id`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='专区通用评论表';

View File

@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.microservices.zone.journals.mapper.ZoneJournalsMapper">
<resultMap type="com.microservices.zone.journals.domain.ZoneJournals" id="ZoneJournalsResult">
<result property="id" column="id" />
<result property="journalizedId" column="journalized_id" />
<result property="journalizedType" column="journalized_type" />
<result property="userId" column="user_id" />
<result property="notes" column="notes" />
<result property="parentId" column="parent_id" />
<result property="replyId" column="reply_id" />
<result property="commentsCount" column="comments_count" />
<result property="attachmentIdentifiers" column="attachment_identifiers"/>
<result property="receiversLogin" column="receivers_login" />
<result property="privateNotes" column="private_notes" />
<result property="delFlag" column="del_flag" />
<result property="createBy" column="create_by" />
<result property="createdOn" column="created_on" />
<result property="updateBy" column="update_by" />
<result property="updatedOn" column="updated_on" />
</resultMap>
<sql id="selectZoneJournalsVo">
select id,
journalized_id,
journalized_type,
user_id,
notes,
parent_id,
reply_id,
comments_count,
attachment_identifiers,
receivers_login,
private_notes,
del_flag,
create_by,
created_on,
update_by,
updated_on
from zone_journals
</sql>
<select id="selectZoneJournalsList" parameterType="com.microservices.zone.journals.domain.ZoneJournals" resultMap="ZoneJournalsResult">
<include refid="selectZoneJournalsVo"/>
<where>
<if test="journalizedId != null "> and journalized_id = #{journalizedId}</if>
<if test="journalizedType != null and journalizedType != ''"> and journalized_type = #{journalizedType}</if>
<if test="userId != null "> and user_id = #{userId}</if>
<if test="privateNotes != null and privateNotes != ''"> and private_notes = #{privateNotes}</if>
and del_flag = '0'
and parent_id is NULL
</where>
order by created_on desc
</select>
<select id="selectZoneJournalsById" parameterType="Long" resultMap="ZoneJournalsResult">
<include refid="selectZoneJournalsVo"/>
where id = #{id}
</select>
<select id="selectChildrenZoneJournalsList" resultMap="ZoneJournalsResult">
<include refid="selectZoneJournalsVo"/>
where parent_id = #{parentId}
and del_flag = '0'
order by created_on asc
</select>
<select id="countByJournalized" resultType="int">
select count(*)
from zone_journals
where journalized_id = #{journalizedId}
and journalized_type = #{journalizedType}
and del_flag = '0'
</select>
<insert id="insertZoneJournals" parameterType="com.microservices.zone.journals.domain.ZoneJournals" useGeneratedKeys="true" keyProperty="id">
insert into zone_journals
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="journalizedId != null">journalized_id,</if>
<if test="journalizedType != null and journalizedType != ''">journalized_type,</if>
<if test="userId != null">user_id,</if>
<if test="notes != null and notes != ''">notes,</if>
<if test="parentId != null">parent_id,</if>
<if test="replyId != null">reply_id,</if>
<if test="commentsCount != null">comments_count,</if>
<if test="attachmentIdentifiers != null">attachment_identifiers,</if>
<if test="receiversLogin != null">receivers_login,</if>
<if test="privateNotes != null">private_notes,</if>
<if test="delFlag != null">del_flag,</if>
<if test="createBy != null">create_by,</if>
<if test="createdOn != null">created_on,</if>
<if test="updateBy != null">update_by,</if>
<if test="updatedOn != null">updated_on,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="journalizedId != null">#{journalizedId},</if>
<if test="journalizedType != null and journalizedType != ''">#{journalizedType},</if>
<if test="userId != null">#{userId},</if>
<if test="notes != null and notes != ''">#{notes},</if>
<if test="parentId != null">#{parentId},</if>
<if test="replyId != null">#{replyId},</if>
<if test="commentsCount != null">#{commentsCount},</if>
<if test="attachmentIdentifiers != null">#{attachmentIdentifiers},</if>
<if test="receiversLogin != null">#{receiversLogin},</if>
<if test="privateNotes != null">#{privateNotes},</if>
<if test="delFlag != null">#{delFlag},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createdOn != null">#{createdOn},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updatedOn != null">#{updatedOn},</if>
</trim>
</insert>
<update id="updateZoneJournals" parameterType="com.microservices.zone.journals.domain.ZoneJournals">
update zone_journals
<trim prefix="SET" suffixOverrides=",">
<if test="notes != null and notes != ''">notes = #{notes},</if>
<if test="attachmentIdentifiers != null">attachment_identifiers = #{attachmentIdentifiers},</if>
<if test="receiversLogin != null">receivers_login = #{receiversLogin},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updatedOn != null">updated_on = #{updatedOn},</if>
</trim>
where id = #{id}
</update>
<update id="updateCommentsCountById">
update zone_journals
set comments_count = (
select count(*)
from zone_journals
where parent_id = #{id}
and del_flag = '0'
)
where id = #{id}
</update>
<update id="updateJournalsIdByTypeAndOldId">
update zone_journals
set journalized_id = #{journalId}
where journalized_type = #{type}
and journalized_id = #{oldJournalId}
</update>
<update id="deleteZoneJournalsById" parameterType="Long">
update zone_journals set del_flag = '2' where id = #{id}
</update>
<update id="deleteZoneJournalsByIds" parameterType="String">
update zone_journals set del_flag = '2' where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</update>
<update id="deleteZoneJournalsByParentId">
update zone_journals set del_flag = '2' where parent_id = #{parentId}
</update>
<update id="deleteZoneJournalsByTypeAndJournalId">
update zone_journals set del_flag = '2'
where journalized_id = #{journalId}
and journalized_type = #{type}
</update>
</mapper>