forked from Gitlink/microservices
Compare commits
13 Commits
master
...
aiResource
| Author | SHA1 | Date |
|---|---|---|
|
|
57dad99399 | |
|
|
965cedc275 | |
|
|
b26825084b | |
|
|
53ff26058f | |
|
|
46b1b9ed81 | |
|
|
20bd41ebf7 | |
|
|
a416b7c856 | |
|
|
d662395f1e | |
|
|
58bdc10a4b | |
|
|
343d31024e | |
|
|
020472c4c1 | |
|
|
356ffd8915 | |
|
|
3a2560f700 |
|
|
@ -10,6 +10,10 @@ import lombok.Data;
|
||||||
@Data
|
@Data
|
||||||
public class SysFile
|
public class SysFile
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* 文件标识
|
||||||
|
*/
|
||||||
|
private Long fileId;
|
||||||
/**
|
/**
|
||||||
* 文件标识
|
* 文件标识
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.microservices.common.security.feign;
|
||||||
import feign.RequestInterceptor;
|
import feign.RequestInterceptor;
|
||||||
import feign.codec.Encoder;
|
import feign.codec.Encoder;
|
||||||
import feign.form.FormEncoder;
|
import feign.form.FormEncoder;
|
||||||
|
import feign.form.spring.SpringFormEncoder;
|
||||||
import org.springframework.beans.factory.ObjectFactory;
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||||
|
|
@ -33,6 +34,6 @@ public class FeignAutoConfiguration
|
||||||
// 重要的是返回类型要是 Encoder 并且实现类必须是 FormEncoder 或者其子类
|
// 重要的是返回类型要是 Encoder 并且实现类必须是 FormEncoder 或者其子类
|
||||||
@Bean
|
@Bean
|
||||||
public Encoder feignFormEncoder() {
|
public Encoder feignFormEncoder() {
|
||||||
return new FormEncoder(new SpringEncoder(messageConverters));
|
return new SpringFormEncoder(new SpringEncoder(messageConverters));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import springfox.documentation.annotations.ApiIgnore;
|
import springfox.documentation.annotations.ApiIgnore;
|
||||||
|
|
@ -33,7 +34,7 @@ public class SysFileController extends BaseController {
|
||||||
*/
|
*/
|
||||||
@InnerAuth
|
@InnerAuth
|
||||||
@PostMapping("upload")
|
@PostMapping("upload")
|
||||||
public R<SysFile> upload(MultipartFile file,
|
public R<SysFile> upload(@RequestPart(value = "file") MultipartFile file,
|
||||||
@RequestParam("type") String type,
|
@RequestParam("type") String type,
|
||||||
@RequestParam("hierarchy") String hierarchy) {
|
@RequestParam("hierarchy") String hierarchy) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -43,6 +44,7 @@ public class SysFileController extends BaseController {
|
||||||
sysFile.setName(fileInfo.getFilePath());
|
sysFile.setName(fileInfo.getFilePath());
|
||||||
sysFile.setUrl(fileInfo.getDownloadUrl());
|
sysFile.setUrl(fileInfo.getDownloadUrl());
|
||||||
sysFile.setFileIdentifier(fileInfo.getFileIdentifier());
|
sysFile.setFileIdentifier(fileInfo.getFileIdentifier());
|
||||||
|
sysFile.setFileId(fileInfo.getFileId());
|
||||||
return R.ok(sysFile);
|
return R.ok(sysFile);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("上传文件失败", e);
|
log.error("上传文件失败", e);
|
||||||
|
|
|
||||||
|
|
@ -271,9 +271,9 @@ public class SysFileInfoServiceImpl implements ISysFileInfoService {
|
||||||
@Override
|
@Override
|
||||||
public void fileDownloadById(Long fileId, HttpServletResponse response, HttpServletRequest request) {
|
public void fileDownloadById(Long fileId, HttpServletResponse response, HttpServletRequest request) {
|
||||||
SysFileInfo sysFileInfo = selectSysFileInfoByFileId(fileId);
|
SysFileInfo sysFileInfo = selectSysFileInfoByFileId(fileId);
|
||||||
if (StringUtils.isNotEmpty(sysFileInfo.getFileIdentifier())) {
|
// if (StringUtils.isNotEmpty(sysFileInfo.getFileIdentifier())) {
|
||||||
throw new ServiceException("当前文件无法下载");
|
// throw new ServiceException("当前文件无法下载");
|
||||||
}
|
// }
|
||||||
fileDownload(sysFileInfo, response);
|
fileDownload(sysFileInfo, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -397,6 +397,7 @@ public class SysFileInfoServiceImpl implements ISysFileInfoService {
|
||||||
hierarchy = validPathSafety(hierarchy);
|
hierarchy = validPathSafety(hierarchy);
|
||||||
}
|
}
|
||||||
boolean isLocalUpload = true;
|
boolean isLocalUpload = true;
|
||||||
|
boolean isHierarchyPath = false;
|
||||||
Long fileId = null;
|
Long fileId = null;
|
||||||
switch (fileType) {
|
switch (fileType) {
|
||||||
case "pms-gitlink":
|
case "pms-gitlink":
|
||||||
|
|
@ -431,6 +432,7 @@ public class SysFileInfoServiceImpl implements ISysFileInfoService {
|
||||||
sysFileInfo.setFileIdentifier(genFileIdentifier());
|
sysFileInfo.setFileIdentifier(genFileIdentifier());
|
||||||
baseFilePath += "/" + fileType + "/" + hierarchy + "/";
|
baseFilePath += "/" + fileType + "/" + hierarchy + "/";
|
||||||
isCommonFileType = false;
|
isCommonFileType = false;
|
||||||
|
isHierarchyPath = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "dms": {
|
case "dms": {
|
||||||
|
|
@ -445,6 +447,13 @@ public class SysFileInfoServiceImpl implements ISysFileInfoService {
|
||||||
isCommonFileType = false;
|
isCommonFileType = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "ai-resource": {
|
||||||
|
sysFileInfo.setFileIdentifier(genFileIdentifier());
|
||||||
|
baseFilePath += "/" + fileType + "/" + hierarchy + "/";
|
||||||
|
isCommonFileType = false;
|
||||||
|
isHierarchyPath = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "zone-identifier": {
|
case "zone-identifier": {
|
||||||
// 生成唯一标识,防止用户通过id递增访问
|
// 生成唯一标识,防止用户通过id递增访问
|
||||||
sysFileInfo.setFileIdentifier(genFileIdentifier());
|
sysFileInfo.setFileIdentifier(genFileIdentifier());
|
||||||
|
|
@ -507,7 +516,7 @@ public class SysFileInfoServiceImpl implements ISysFileInfoService {
|
||||||
sysFileInfo.setFileOriginName(session.getFileName());
|
sysFileInfo.setFileOriginName(session.getFileName());
|
||||||
}
|
}
|
||||||
if (!isCommonFileType) {
|
if (!isCommonFileType) {
|
||||||
if ("pms".equals(fileType)) {
|
if (isHierarchyPath) {
|
||||||
filePath = EscapeUtil.removeExtraSlashOfUrl("/" + fileType + "/" + hierarchy + "/" + filePath);
|
filePath = EscapeUtil.removeExtraSlashOfUrl("/" + fileType + "/" + hierarchy + "/" + filePath);
|
||||||
} else {
|
} else {
|
||||||
filePath = EscapeUtil.removeExtraSlashOfUrl("/" + fileType + "/" + filePath);
|
filePath = EscapeUtil.removeExtraSlashOfUrl("/" + fileType + "/" + filePath);
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,14 @@ import com.microservices.common.security.utils.SecurityUtils;
|
||||||
import com.microservices.zone.detail.domain.vo.ZoneFrontRoleVo;
|
import com.microservices.zone.detail.domain.vo.ZoneFrontRoleVo;
|
||||||
import com.microservices.zone.detail.service.IZoneDetailService;
|
import com.microservices.zone.detail.service.IZoneDetailService;
|
||||||
import com.microservices.zone.resource.domain.ZoneResource;
|
import com.microservices.zone.resource.domain.ZoneResource;
|
||||||
import com.microservices.zone.resource.domain.vo.ZoneResourceDataVo;
|
import com.microservices.zone.resource.domain.vo.*;
|
||||||
import com.microservices.zone.resource.domain.vo.ZoneResourceInputVo;
|
import com.microservices.zone.resource.service.IAiSkillParseService;
|
||||||
import com.microservices.zone.resource.domain.vo.ZoneResourceSearchVo;
|
|
||||||
import com.microservices.zone.resource.domain.vo.ZoneResourceUpdateVo;
|
|
||||||
import com.microservices.zone.resource.service.IZoneResourceService;
|
import com.microservices.zone.resource.service.IZoneResourceService;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.*;
|
||||||
import io.swagger.annotations.ApiOperation;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author otto
|
* @author otto
|
||||||
|
|
@ -32,6 +31,9 @@ public class FrontController extends BaseController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private IZoneResourceService zoneResourceService;
|
private IZoneResourceService zoneResourceService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IAiSkillParseService aiSkillParseService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IZoneDetailService zoneDetailService;
|
private IZoneDetailService zoneDetailService;
|
||||||
|
|
||||||
|
|
@ -103,4 +105,79 @@ public class FrontController extends BaseController {
|
||||||
public GenericsAjaxResult<ZoneFrontRoleVo> checkCurrentRole(@PathVariable Long zoneId) {
|
public GenericsAjaxResult<ZoneFrontRoleVo> checkCurrentRole(@PathVariable Long zoneId) {
|
||||||
return genericsSuccess(zoneDetailService.checkCurrentRoleInZone(zoneId));
|
return genericsSuccess(zoneDetailService.checkCurrentRoleInZone(zoneId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== Skill资源广场 - 我的资源管理 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传ZIP并解析
|
||||||
|
*/
|
||||||
|
// @RequiresPermissions("zone:front:skill:add")
|
||||||
|
@PostMapping("/skill/parse")
|
||||||
|
@ApiOperation("上传ZIP并解析Skill包")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "file", value = "文件实体", paramType = "form", dataType = "_file"),
|
||||||
|
})
|
||||||
|
public GenericsAjaxResult<AiSkillParseResultVo> parseSkillZip(@RequestPart("file") MultipartFile file) {
|
||||||
|
return genericsSuccess(aiSkillParseService.parseSkillZip(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建Skill资源
|
||||||
|
*/
|
||||||
|
// @RequiresPermissions("zone:front:skill:add")
|
||||||
|
@Log(title = "创建Skill资源", businessType = BusinessType.INSERT)
|
||||||
|
@PostMapping("/skill")
|
||||||
|
@ApiOperation("创建Skill资源")
|
||||||
|
public AjaxResult addSkill(@RequestBody @Validated AiSkillInputVo inputVo) {
|
||||||
|
return success(zoneResourceService.insertAiSkillResource(inputVo));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新提交被驳回的Skill资源
|
||||||
|
*/
|
||||||
|
// @RequiresPermissions("zone:front:skill:add")
|
||||||
|
@Log(title = "重新提交Skill资源", businessType = BusinessType.UPDATE)
|
||||||
|
@PutMapping("/skillResource/{id}/resubmit")
|
||||||
|
@ApiOperation(value = "重新提交被驳回的资源")
|
||||||
|
public AjaxResult resubmitAiResource(
|
||||||
|
@ApiParam(value = "资源ID", required = true) @PathVariable("id") Long id) {
|
||||||
|
return toAjax(zoneResourceService.resubmitAiResource(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤回审核中的Skill资源
|
||||||
|
*/
|
||||||
|
// @RequiresPermissions("zone:front:skill:edit")
|
||||||
|
@Log(title = "撤回Skill资源", businessType = BusinessType.UPDATE)
|
||||||
|
@PutMapping("/skillResource/{id}/withdraw")
|
||||||
|
@ApiOperation(value = "撤回审核中的资源")
|
||||||
|
public AjaxResult withdrawAiResource(
|
||||||
|
@ApiParam(value = "资源ID", required = true) @PathVariable("id") Long id) {
|
||||||
|
return toAjax(zoneResourceService.withdrawAiResource(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下架资源
|
||||||
|
*/
|
||||||
|
// @RequiresPermissions("ai:resource:audit")
|
||||||
|
@Log(title = "下架资源", businessType = BusinessType.UPDATE)
|
||||||
|
@PutMapping("/{id}/takeDown")
|
||||||
|
@ApiOperation("下架资源")
|
||||||
|
public AjaxResult takeDownResource(
|
||||||
|
@ApiParam(value = "资源ID", required = true) @PathVariable("id") Long id,
|
||||||
|
@RequestParam(value = "reason", required = false) String reason) {
|
||||||
|
return toAjax(zoneResourceService.takeDownAiResource(id, reason));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 点赞/取消点赞
|
||||||
|
*/
|
||||||
|
// @RequiresPermissions("ai:resource:skill:query")
|
||||||
|
@PostMapping("/{id}/like")
|
||||||
|
@ApiOperation("点赞/取消点赞AI资源")
|
||||||
|
public AjaxResult toggleLike(
|
||||||
|
@ApiParam(value = "资源ID", required = true) @PathVariable("id") Long id) {
|
||||||
|
boolean liked = zoneResourceService.toggleAiResourceLike(id);
|
||||||
|
return success(liked ? "已点赞" : "已取消点赞");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.ZoneBaseDataVo;
|
||||||
import com.microservices.zone.detail.domain.vo.ZoneDetailDataVo;
|
import com.microservices.zone.detail.domain.vo.ZoneDetailDataVo;
|
||||||
import com.microservices.zone.detail.service.IZoneDetailService;
|
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.ZoneMemberDataVo;
|
||||||
import com.microservices.zone.member.domain.vo.ZoneMemberOpenSearchVo;
|
import com.microservices.zone.member.domain.vo.ZoneMemberOpenSearchVo;
|
||||||
import com.microservices.zone.member.domain.vo.ZoneMemberOverviewVo;
|
import com.microservices.zone.member.domain.vo.ZoneMemberOverviewVo;
|
||||||
|
|
@ -34,6 +37,7 @@ import com.microservices.zone.resource.domain.ZoneResourceDomain;
|
||||||
import com.microservices.zone.resource.domain.ZoneResourceType;
|
import com.microservices.zone.resource.domain.ZoneResourceType;
|
||||||
import com.microservices.zone.resource.domain.vo.ZoneResourceDataVo;
|
import com.microservices.zone.resource.domain.vo.ZoneResourceDataVo;
|
||||||
import com.microservices.zone.resource.domain.vo.ZoneResourceOpenSearchVo;
|
import com.microservices.zone.resource.domain.vo.ZoneResourceOpenSearchVo;
|
||||||
|
import com.microservices.zone.resource.enums.AiResourceType;
|
||||||
import com.microservices.zone.resource.service.IZoneResourceDomainService;
|
import com.microservices.zone.resource.service.IZoneResourceDomainService;
|
||||||
import com.microservices.zone.resource.service.IZoneResourceService;
|
import com.microservices.zone.resource.service.IZoneResourceService;
|
||||||
import com.microservices.zone.resource.service.IZoneResourceTypeService;
|
import com.microservices.zone.resource.service.IZoneResourceTypeService;
|
||||||
|
|
@ -241,7 +245,7 @@ public class OpenController extends BaseController {
|
||||||
@GetMapping("/{zoneId}/member/homePageList")
|
@GetMapping("/{zoneId}/member/homePageList")
|
||||||
@ApiOperation("获取首页特色专区会员列表")
|
@ApiOperation("获取首页特色专区会员列表")
|
||||||
public GenericsTableDataInfo<ZoneMemberDataVo> homePageMemberList(@ApiParam(name = "zoneId", value = "专区Id", required = true) @PathVariable("zoneId") Long zoneId) {
|
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);
|
return getGenericsDataTable(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -411,4 +415,67 @@ public class OpenController extends BaseController {
|
||||||
List<ZoneSpecialProjectReport> list = zoneSpecialProjectReportService.selectZoneSpecialProjectReportList(zoneSpecialProjectReportSearchVo.toZoneSpecialProjectReport());
|
List<ZoneSpecialProjectReport> list = zoneSpecialProjectReportService.selectZoneSpecialProjectReportList(zoneSpecialProjectReportSearchVo.toZoneSpecialProjectReport());
|
||||||
return getGenericsDataTable(list);
|
return getGenericsDataTable(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== AI资源广场 - 开放接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 广场首页-Skill推荐列表(前4名)
|
||||||
|
*/
|
||||||
|
@GetMapping("/ai-square/skills")
|
||||||
|
@ApiOperation("广场首页-Skill推荐列表")
|
||||||
|
public GenericsAjaxResult<List<ZoneResourceDataVo>> skillRecommendations() {
|
||||||
|
return genericsSuccess(zoneResourceService.selectSkillRecommendations(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公开-Skill详情
|
||||||
|
*/
|
||||||
|
@GetMapping("/ai-square/skill/{id}")
|
||||||
|
@ApiOperation("公开-Skill详情")
|
||||||
|
public GenericsAjaxResult<ZoneResourceDataVo> skillDetail(
|
||||||
|
@ApiParam(name = "id", value = "资源ID", required = true) @PathVariable("id") Long id) {
|
||||||
|
// 增加浏览次数
|
||||||
|
zoneResourceService.incrementViewCount(id);
|
||||||
|
return genericsSuccess(zoneResourceService.selectAiSkillResourceById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公开-Skill列表(搜索)
|
||||||
|
*/
|
||||||
|
@GetMapping("/ai-square/skill/list")
|
||||||
|
@ApiOperation("公开-Skill列表搜索")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "pageNum", value = "当前记录起始索引", paramType = "query", dataType = "Integer"),
|
||||||
|
@ApiImplicitParam(name = "pageSize", value = "每页显示记录数", paramType = "query", dataType = "Integer")
|
||||||
|
})
|
||||||
|
public GenericsTableDataInfo<ZoneResourceDataVo> skillList(ZoneResource zoneResource) {
|
||||||
|
// 公开接口只查询已发布的
|
||||||
|
zoneResource.setAuditStatus("1");
|
||||||
|
zoneResource.setResourceCategory(AiResourceType.SKILL.getCode());
|
||||||
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
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.GenericsTableDataInfo;
|
||||||
|
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 GenericsTableDataInfo<ZoneJournalsParentDataVo> list(ZoneJournalsSearchVo searchVo) {
|
||||||
|
startPage();
|
||||||
|
List<ZoneJournalsParentDataVo> list = zoneJournalsService.selectJournalsList(searchVo);
|
||||||
|
return new GenericsTableDataInfo<>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.microservices.zone.journals.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.microservices.common.core.web.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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,414 @@
|
||||||
|
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.getParentId() != 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 = FeignUtils.getReturnData(
|
||||||
|
remoteFileService.getFileListByIdentifier(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 = FeignUtils.getReturnData(
|
||||||
|
remoteFileService.getFileListByIdentifier(attachmentIdentifiers)
|
||||||
|
);
|
||||||
|
return CollectionUtils.isEmpty(fileInfos) ? new ArrayList<>() : fileInfos;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.microservices.zone.resource.controller;
|
||||||
|
|
||||||
|
import com.microservices.common.core.web.controller.BaseController;
|
||||||
|
import com.microservices.common.core.web.domain.AjaxResult;
|
||||||
|
import com.microservices.common.core.web.domain.GenericsAjaxResult;
|
||||||
|
import com.microservices.common.core.web.page.GenericsTableDataInfo;
|
||||||
|
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.resource.domain.ZoneResource;
|
||||||
|
import com.microservices.zone.resource.domain.vo.*;
|
||||||
|
import com.microservices.zone.resource.service.IAiSkillParseService;
|
||||||
|
import com.microservices.zone.resource.service.IZoneResourceService;
|
||||||
|
import io.swagger.annotations.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI资源管理Controller
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/ai-resource")
|
||||||
|
@Api(tags = "AI资源管理接口")
|
||||||
|
public class AiResourceController extends BaseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IZoneResourceService zoneResourceService;
|
||||||
|
@Autowired
|
||||||
|
private IAiSkillParseService aiSkillParseService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传ZIP并解析
|
||||||
|
*/
|
||||||
|
// @RequiresPermissions("ai:resource:skill:add")
|
||||||
|
@PostMapping("/skill/parse")
|
||||||
|
@ApiOperation("上传ZIP并解析Skill包")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "file", value = "文件实体", paramType = "form", dataType = "_file"),
|
||||||
|
})
|
||||||
|
public GenericsAjaxResult<AiSkillParseResultVo> parseSkillZip(@RequestPart("file") MultipartFile file) {
|
||||||
|
return genericsSuccess(aiSkillParseService.parseSkillZip(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建Skill资源
|
||||||
|
*/
|
||||||
|
// @RequiresPermissions("ai:resource:skill:add")
|
||||||
|
@Log(title = "创建Skill资源", businessType = BusinessType.INSERT)
|
||||||
|
@PostMapping("/skill")
|
||||||
|
@ApiOperation("创建Skill资源")
|
||||||
|
public AjaxResult addSkill(@RequestBody @Validated AiSkillInputVo inputVo) {
|
||||||
|
return success(zoneResourceService.insertAiSkillResource(inputVo));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改Skill资源
|
||||||
|
*/
|
||||||
|
// @RequiresPermissions("ai:resource:skill:edit")
|
||||||
|
@Log(title = "修改Skill资源", businessType = BusinessType.UPDATE)
|
||||||
|
@PutMapping("/skill")
|
||||||
|
@ApiOperation("修改Skill资源")
|
||||||
|
public AjaxResult editSkill(@RequestBody @Validated AiSkillUpdateVo updateVo) {
|
||||||
|
return success(zoneResourceService.updateAiSkillResource(updateVo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ import com.microservices.zone.resource.domain.vo.*;
|
||||||
import com.microservices.zone.resource.service.IZoneResourceService;
|
import com.microservices.zone.resource.service.IZoneResourceService;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import io.swagger.annotations.ApiParam;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
|
@ -106,4 +107,17 @@ public class ZoneResourceController extends BaseController {
|
||||||
public AjaxResult remove(@PathVariable Long[] ids) {
|
public AjaxResult remove(@PathVariable Long[] ids) {
|
||||||
return toAjax(zoneResourceService.deleteZoneResourceByIds(ids));
|
return toAjax(zoneResourceService.deleteZoneResourceByIds(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下架资源
|
||||||
|
*/
|
||||||
|
// @RequiresPermissions("ai:resource:audit")
|
||||||
|
@Log(title = "下架资源", businessType = BusinessType.UPDATE)
|
||||||
|
@PutMapping("/{id}/takeDown")
|
||||||
|
@ApiOperation("下架资源")
|
||||||
|
public AjaxResult takeDownResource(
|
||||||
|
@ApiParam(value = "资源ID", required = true) @PathVariable("id") Long id,
|
||||||
|
@RequestParam(value = "reason", required = false) String reason) {
|
||||||
|
return toAjax(zoneResourceService.takeDownAiResource(id, reason));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.microservices.zone.resource.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI资源评论实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AiResourceComment {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private Long resourceId;
|
||||||
|
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
private Long replyId;
|
||||||
|
|
||||||
|
private String attachmentIdentifiers;
|
||||||
|
|
||||||
|
private Integer likeCount;
|
||||||
|
|
||||||
|
private String delFlag;
|
||||||
|
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.microservices.zone.resource.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI资源点赞实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AiResourceLike {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private Long resourceId;
|
||||||
|
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
private Date createTime;
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.microservices.zone.resource.domain;
|
package com.microservices.zone.resource.domain;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.microservices.common.core.annotation.Excel;
|
import com.microservices.common.core.annotation.Excel;
|
||||||
import com.microservices.common.core.utils.bean.BeanUtils;
|
import com.microservices.common.core.utils.bean.BeanUtils;
|
||||||
import com.microservices.zone.detail.domain.ZoneSort;
|
import com.microservices.zone.detail.domain.ZoneSort;
|
||||||
import com.microservices.zone.resource.domain.vo.ZoneResourceDataVo;
|
import com.microservices.zone.resource.domain.vo.ZoneResourceDataVo;
|
||||||
|
import com.microservices.zone.resource.utils.ResourceExtendDataUtils;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
|
@ -93,8 +95,26 @@ public class ZoneResource extends ZoneSort {
|
||||||
@ApiModelProperty(value = "资源标签")
|
@ApiModelProperty(value = "资源标签")
|
||||||
private String keywords;
|
private String keywords;
|
||||||
|
|
||||||
@ApiModelProperty(value = "拓展数据")
|
@ApiModelProperty(value = "拓展数据(JSON格式)")
|
||||||
private String extendData;
|
private JSONObject extendData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源分类:SKILL/MODEL/DATASET(NULL表示原有专区资源)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "资源分类:SKILL/MODEL/DATASET")
|
||||||
|
private String resourceCategory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浏览次数
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "浏览次数")
|
||||||
|
private Integer viewCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点赞次数
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "点赞次数")
|
||||||
|
private Integer likeCount;
|
||||||
|
|
||||||
public ZoneResourceDataVo toZoneResourceDataVo() {
|
public ZoneResourceDataVo toZoneResourceDataVo() {
|
||||||
ZoneResourceDataVo target = new ZoneResourceDataVo();
|
ZoneResourceDataVo target = new ZoneResourceDataVo();
|
||||||
|
|
@ -105,4 +125,34 @@ public class ZoneResource extends ZoneSort {
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== extendData 便捷方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 SKILL.md 内容(从 extendData 中提取)
|
||||||
|
*/
|
||||||
|
public String getSkillContent() {
|
||||||
|
return ResourceExtendDataUtils.extractSkillContent(this.extendData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取目录树 JSON(从 extendData 中提取)
|
||||||
|
*/
|
||||||
|
public JSONObject getSkillFileTree() {
|
||||||
|
return ResourceExtendDataUtils.extractSkillFileTree(this.extendData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 SKILL.md 内容(更新 extendData)
|
||||||
|
*/
|
||||||
|
public void setSkillContent(String skillContent) {
|
||||||
|
this.extendData = ResourceExtendDataUtils.updateSkillContent(this.extendData, skillContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置目录树 JSON(更新 extendData)
|
||||||
|
*/
|
||||||
|
public void setSkillFileTree(JSONObject fileTreeJson) {
|
||||||
|
this.extendData = ResourceExtendDataUtils.updateSkillFileTree(this.extendData, fileTreeJson);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.microservices.zone.resource.domain.vo;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.microservices.common.core.utils.StringUtils;
|
||||||
|
import com.microservices.common.core.utils.bean.BeanUtils;
|
||||||
|
import com.microservices.zone.resource.domain.ZoneResource;
|
||||||
|
import com.microservices.zone.resource.enums.AiResourceType;
|
||||||
|
import com.microservices.zone.resource.utils.ResourceExtendDataUtils;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skill提报输入VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("Skill提报输入")
|
||||||
|
public class AiSkillInputVo {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "Skill名称", required = true)
|
||||||
|
@NotBlank(message = "Skill名称不能为空")
|
||||||
|
@Size(max = 100, message = "Skill名称不能超过100个字符")
|
||||||
|
private String name;
|
||||||
|
@ApiModelProperty(value = "资源领域Id", required = true)
|
||||||
|
@NotNull
|
||||||
|
private Long domainId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "Skill简介", required = true)
|
||||||
|
@NotBlank(message = "Skill简介不能为空")
|
||||||
|
@Size(max = 500, message = "Skill简介不能超过500个字符")
|
||||||
|
private String summary;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "资源标签,逗号分隔")
|
||||||
|
private String keywords;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "Skill文件Id")
|
||||||
|
private Long fileId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "SKILL.md内容", required = true)
|
||||||
|
@NotBlank(message = "SKILL.md内容不能为空")
|
||||||
|
private String skillContent;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "目录树JSON")
|
||||||
|
private String fileTreeJson;
|
||||||
|
@ApiModelProperty(value = "所属专区", required = true)
|
||||||
|
@NotNull
|
||||||
|
private Long zoneId;
|
||||||
|
|
||||||
|
public ZoneResourceInputVo toZoneResourceInputVo() {
|
||||||
|
ZoneResourceInputVo target = new ZoneResourceInputVo();
|
||||||
|
BeanUtils.copyProperties(this, target, "skillContent", "fileTreeJson");
|
||||||
|
target.setFileIds(String.valueOf(fileId));
|
||||||
|
|
||||||
|
// 将 Skill 特有字段存入 extendData(JSONObject)
|
||||||
|
JSONObject fileTreeJsonObj = StringUtils.isEmpty(fileTreeJson)
|
||||||
|
? null
|
||||||
|
: JSONObject.parseObject(fileTreeJson);
|
||||||
|
JSONObject extendData = ResourceExtendDataUtils.buildSkillExtendData(
|
||||||
|
skillContent, fileTreeJsonObj);
|
||||||
|
target.setExtendData(extendData.toJSONString());
|
||||||
|
target.setResourceCategory(AiResourceType.SKILL.getCode());
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.microservices.zone.resource.domain.vo;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skill ZIP解析结果VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("Skill ZIP解析结果")
|
||||||
|
public class AiSkillParseResultVo {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "SKILL.md文件内容")
|
||||||
|
private String skillMdContent;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "ZIP包目录树结构JSON")
|
||||||
|
private String fileTreeJson;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "上传后的文件id")
|
||||||
|
private Long fileId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "Skill描述(从YAML front matter中提取)")
|
||||||
|
private String summary;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.microservices.zone.resource.domain.vo;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.microservices.common.core.utils.StringUtils;
|
||||||
|
import com.microservices.common.core.utils.bean.BeanUtils;
|
||||||
|
import com.microservices.zone.resource.domain.ZoneResource;
|
||||||
|
import com.microservices.zone.resource.utils.ResourceExtendDataUtils;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skill修改输入VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("Skill修改输入")
|
||||||
|
public class AiSkillUpdateVo {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "资源Id", required = true)
|
||||||
|
@NotNull(message = "资源Id不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "Skill名称")
|
||||||
|
@Size(max = 100, message = "Skill名称不能超过100个字符")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "Skill简介")
|
||||||
|
@Size(max = 500, message = "Skill简介不能超过500个字符")
|
||||||
|
private String summary;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "Skill文件Id")
|
||||||
|
private Long fileId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "SKILL.md内容")
|
||||||
|
private String skillContent;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "目录树JSON")
|
||||||
|
private String fileTreeJson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为 ZoneResource(合并 extendData)
|
||||||
|
*
|
||||||
|
* @param existing 现有资源对象,用于获取原有 extendData
|
||||||
|
* @return 合并后的 ZoneResource
|
||||||
|
*/
|
||||||
|
public ZoneResourceUpdateVo toZoneResourceUpdateVo(ZoneResource existing) {
|
||||||
|
ZoneResourceUpdateVo target = new ZoneResourceUpdateVo();
|
||||||
|
BeanUtils.copyProperties(this, target);
|
||||||
|
if (fileId == null) {
|
||||||
|
target.setFileIds(existing.getFileIds());
|
||||||
|
} else {
|
||||||
|
target.setFileIds(String.valueOf(fileId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取现有 extendData 或创建新的
|
||||||
|
JSONObject extendData = existing.getExtendData();
|
||||||
|
if (extendData == null) {
|
||||||
|
extendData = new JSONObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 skillContent
|
||||||
|
if (skillContent != null) {
|
||||||
|
extendData = ResourceExtendDataUtils.updateSkillContent(extendData, skillContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 fileTree
|
||||||
|
if (fileTreeJson != null) {
|
||||||
|
JSONObject fileTreeObj = JSONObject.parseObject(fileTreeJson);
|
||||||
|
extendData = ResourceExtendDataUtils.updateSkillFileTree(extendData, fileTreeObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
target.setExtendData(extendData.toJSONString());
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package com.microservices.zone.resource.domain.vo;
|
package com.microservices.zone.resource.domain.vo;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.microservices.system.api.domain.SimpleSysUser;
|
import com.microservices.system.api.domain.SimpleSysUser;
|
||||||
import com.microservices.zone.resource.domain.ZoneResourceType;
|
import com.microservices.zone.resource.domain.ZoneResourceType;
|
||||||
|
import com.microservices.zone.resource.utils.ResourceExtendDataUtils;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
@ -73,6 +75,18 @@ public class ZoneResourceDataVo {
|
||||||
private Date auditTime;
|
private Date auditTime;
|
||||||
@ApiModelProperty(value = "审核人")
|
@ApiModelProperty(value = "审核人")
|
||||||
private String auditBy;
|
private String auditBy;
|
||||||
@ApiModelProperty(value = "拓展数据")
|
@ApiModelProperty(value = "拓展数据(JSON格式)")
|
||||||
private String extendData;
|
private JSONObject extendData;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "资源分类:SKILL/MODEL/DATASET")
|
||||||
|
private String resourceCategory;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "审核意见/驳回原因")
|
||||||
|
private String auditReason;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "浏览次数")
|
||||||
|
private Integer viewCount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "点赞次数")
|
||||||
|
private Integer likeCount;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.microservices.zone.resource.domain.vo;
|
package com.microservices.zone.resource.domain.vo;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.microservices.common.core.utils.bean.BeanUtils;
|
import com.microservices.common.core.utils.bean.BeanUtils;
|
||||||
import com.microservices.zone.resource.domain.ZoneResource;
|
import com.microservices.zone.resource.domain.ZoneResource;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
|
|
@ -33,6 +34,11 @@ public class ZoneResourceInputVo {
|
||||||
@ApiModelProperty(value = "所属专区", required = true)
|
@ApiModelProperty(value = "所属专区", required = true)
|
||||||
@NotNull
|
@NotNull
|
||||||
private Long zoneId;
|
private Long zoneId;
|
||||||
|
/**
|
||||||
|
* 资源分类:SKILL/MODEL/DATASET(NULL表示原有专区资源)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "资源分类:SKILL/MODEL/DATASET", hidden = true)
|
||||||
|
private String resourceCategory = null;
|
||||||
|
|
||||||
@ApiModelProperty(value = "拓展数据")
|
@ApiModelProperty(value = "拓展数据")
|
||||||
private String extendData;
|
private String extendData;
|
||||||
|
|
@ -40,6 +46,7 @@ public class ZoneResourceInputVo {
|
||||||
public ZoneResource toZoneResource() {
|
public ZoneResource toZoneResource() {
|
||||||
ZoneResource target = new ZoneResource();
|
ZoneResource target = new ZoneResource();
|
||||||
BeanUtils.copyProperties(this, target);
|
BeanUtils.copyProperties(this, target);
|
||||||
|
target.setExtendData(JSONObject.parseObject(extendData));
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ public class ZoneResourceOpenSearchVo extends BaseEntity {
|
||||||
@ApiModelProperty(value = "是否根据下载次数降序排序(与orderByColumn互斥,该属性优先级更高)")
|
@ApiModelProperty(value = "是否根据下载次数降序排序(与orderByColumn互斥,该属性优先级更高)")
|
||||||
private Boolean orderByDownloadCount = false;
|
private Boolean orderByDownloadCount = false;
|
||||||
|
|
||||||
@ApiModelProperty(value = "审核状态(0:待审核,1:审核通过 2:审核不通过)")
|
@ApiModelProperty(value = "审核状态(0:待审核,1:审核通过 2:审核不通过 3:已下架)")
|
||||||
private String auditStatus;
|
private String auditStatus;
|
||||||
|
|
||||||
public ZoneResource toZoneResource() {
|
public ZoneResource toZoneResource() {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@ public class ZoneResourceSearchVo extends ZoneResourceOpenSearchVo {
|
||||||
|
|
||||||
@ApiModelProperty(value = "审核状态(0:待审核,1:审核通过 2:审核不通过)")
|
@ApiModelProperty(value = "审核状态(0:待审核,1:审核通过 2:审核不通过)")
|
||||||
private String auditStatus;
|
private String auditStatus;
|
||||||
|
/**
|
||||||
|
* 资源分类:SKILL/MODEL/DATASET(NULL表示原有专区资源)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "资源分类:SKILL/MODEL/DATASET")
|
||||||
|
private String resourceCategory;
|
||||||
|
|
||||||
public ZoneResource toZoneResource() {
|
public ZoneResource toZoneResource() {
|
||||||
ZoneResource zoneResource = super.toZoneResource();
|
ZoneResource zoneResource = super.toZoneResource();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.microservices.zone.resource.domain.vo;
|
package com.microservices.zone.resource.domain.vo;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.microservices.common.core.utils.bean.BeanUtils;
|
import com.microservices.common.core.utils.bean.BeanUtils;
|
||||||
import com.microservices.zone.resource.domain.ZoneResource;
|
import com.microservices.zone.resource.domain.ZoneResource;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
|
|
@ -44,6 +45,7 @@ public class ZoneResourceUpdateVo {
|
||||||
public ZoneResource toZoneResource() {
|
public ZoneResource toZoneResource() {
|
||||||
ZoneResource target = new ZoneResource();
|
ZoneResource target = new ZoneResource();
|
||||||
BeanUtils.copyProperties(this, target);
|
BeanUtils.copyProperties(this, target);
|
||||||
|
target.setExtendData(JSONObject.parseObject(extendData));
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.microservices.zone.resource.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI资源类型枚举
|
||||||
|
*/
|
||||||
|
public enum AiResourceType {
|
||||||
|
|
||||||
|
SKILL("SKILL", "Skill技能");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
|
AiResourceType(String code, String desc) {
|
||||||
|
this.code = code;
|
||||||
|
this.desc = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDesc() {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.microservices.zone.resource.handler;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import org.apache.ibatis.type.BaseTypeHandler;
|
||||||
|
import org.apache.ibatis.type.JdbcType;
|
||||||
|
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||||
|
import org.apache.ibatis.type.MappedTypes;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSONObject 与 String 之间的类型转换处理器
|
||||||
|
* 用于 extendData 字段:代码中操作 JSONObject,数据库存储 TEXT
|
||||||
|
*/
|
||||||
|
@MappedTypes(JSONObject.class)
|
||||||
|
@MappedJdbcTypes(JdbcType.VARCHAR)
|
||||||
|
public class JsonObjectTypeHandler extends BaseTypeHandler<JSONObject> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNonNullParameter(PreparedStatement ps, int i, JSONObject parameter, JdbcType jdbcType) throws SQLException {
|
||||||
|
ps.setString(i, parameter.toJSONString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||||
|
String value = rs.getString(columnName);
|
||||||
|
return parseJSONObject(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||||
|
String value = rs.getString(columnIndex);
|
||||||
|
return parseJSONObject(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||||
|
String value = cs.getString(columnIndex);
|
||||||
|
return parseJSONObject(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject parseJSONObject(String value) {
|
||||||
|
if (value == null || value.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSONObject.parseObject(value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.microservices.zone.resource.mapper;
|
||||||
|
|
||||||
|
import com.microservices.zone.resource.domain.AiResourceLike;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI资源点赞Mapper接口
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface AiResourceLikeMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询用户对指定资源的点赞记录
|
||||||
|
*
|
||||||
|
* @param resourceId 资源ID
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 点赞记录
|
||||||
|
*/
|
||||||
|
AiResourceLike selectByResourceAndUser(@Param("resourceId") Long resourceId, @Param("userId") Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增点赞记录
|
||||||
|
*
|
||||||
|
* @param aiResourceLike 点赞记录
|
||||||
|
* @return 影响行数
|
||||||
|
*/
|
||||||
|
int insertLike(AiResourceLike aiResourceLike);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除点赞记录
|
||||||
|
*
|
||||||
|
* @param resourceId 资源ID
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 影响行数
|
||||||
|
*/
|
||||||
|
int deleteLike(@Param("resourceId") Long resourceId, @Param("userId") Long userId);
|
||||||
|
}
|
||||||
|
|
@ -95,4 +95,39 @@ public interface ZoneResourceMapper {
|
||||||
* @param zoneId 专区Id
|
* @param zoneId 专区Id
|
||||||
*/
|
*/
|
||||||
void deleteZoneResourceByZoneId(Long zoneId);
|
void deleteZoneResourceByZoneId(Long zoneId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过名称查询Skill资源(resource_category='SKILL')
|
||||||
|
*
|
||||||
|
* @param name Skill名称
|
||||||
|
* @return 资源
|
||||||
|
*/
|
||||||
|
ZoneResource selectSkillResourceByName(@Param("name") String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询已发布的Skill推荐列表
|
||||||
|
*
|
||||||
|
* @param resourceCategory 资源分类
|
||||||
|
* @param auditStatus 审核状态
|
||||||
|
* @param limit 数量限制
|
||||||
|
* @return 资源列表
|
||||||
|
*/
|
||||||
|
List<ZoneResource> selectSkillRecommendations(@Param("resourceCategory") String resourceCategory,
|
||||||
|
@Param("auditStatus") String auditStatus,
|
||||||
|
@Param("limit") int limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加浏览次数
|
||||||
|
*
|
||||||
|
* @param id 资源ID
|
||||||
|
*/
|
||||||
|
int incrementViewCount(@Param("id") Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加点赞次数
|
||||||
|
*
|
||||||
|
* @param id 资源ID
|
||||||
|
* @param delta 变化量(正数加、负数减)
|
||||||
|
*/
|
||||||
|
int updateLikeCount(@Param("id") Long id, @Param("delta") int delta);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.microservices.zone.resource.service;
|
||||||
|
|
||||||
|
import com.microservices.zone.resource.domain.vo.AiSkillParseResultVo;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skill ZIP解析服务接口
|
||||||
|
*/
|
||||||
|
public interface IAiSkillParseService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析Skill ZIP包
|
||||||
|
* 1. 校验ZIP内必须包含 SKILL.md
|
||||||
|
* 2. 提取 SKILL.md 内容
|
||||||
|
* 3. 生成目录树结构 JSON
|
||||||
|
*
|
||||||
|
* @param zipFile 上传的ZIP文件
|
||||||
|
* @return 解析结果
|
||||||
|
*/
|
||||||
|
AiSkillParseResultVo parseSkillZip(MultipartFile zipFile);
|
||||||
|
}
|
||||||
|
|
@ -138,4 +138,84 @@ public interface IZoneResourceService {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Integer selectMyZoneResourceCount(ZoneResource zoneResourceSearch);
|
Integer selectMyZoneResourceCount(ZoneResource zoneResourceSearch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建Skill资源(AI资源广场)
|
||||||
|
*
|
||||||
|
* @param inputVo Skill提报输入
|
||||||
|
* @return 资源数据
|
||||||
|
*/
|
||||||
|
ZoneResourceDataVo insertAiSkillResource(AiSkillInputVo inputVo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改Skill资源(AI资源广场)
|
||||||
|
*
|
||||||
|
* @param updateVo Skill修改输入
|
||||||
|
* @return 资源数据
|
||||||
|
*/
|
||||||
|
ZoneResourceDataVo updateAiSkillResource(AiSkillUpdateVo updateVo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询Skill资源详情(AI资源广场)
|
||||||
|
*
|
||||||
|
* @param id 资源ID
|
||||||
|
* @return 资源数据
|
||||||
|
*/
|
||||||
|
ZoneResourceDataVo selectAiSkillResourceById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询Skill资源列表(AI资源广场)
|
||||||
|
*
|
||||||
|
* @param zoneResource 查询条件
|
||||||
|
* @return 分页数据
|
||||||
|
*/
|
||||||
|
GenericsTableDataInfo<ZoneResourceDataVo> selectAiSkillResourceList(ZoneResource zoneResource);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下架AI资源
|
||||||
|
*
|
||||||
|
* @param id 资源ID
|
||||||
|
* @param reason 下架原因
|
||||||
|
* @return 影响行数
|
||||||
|
*/
|
||||||
|
int takeDownAiResource(Long id, String reason);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新提交被驳回的AI资源
|
||||||
|
*
|
||||||
|
* @param id 资源ID
|
||||||
|
* @return 影响行数
|
||||||
|
*/
|
||||||
|
int resubmitAiResource(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤回审核中的AI资源
|
||||||
|
*
|
||||||
|
* @param id 资源ID
|
||||||
|
* @return 影响行数
|
||||||
|
*/
|
||||||
|
int withdrawAiResource(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询Skill推荐列表
|
||||||
|
*
|
||||||
|
* @param limit 数量限制
|
||||||
|
* @return 资源列表
|
||||||
|
*/
|
||||||
|
List<ZoneResourceDataVo> selectSkillRecommendations(int limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI资源点赞/取消点赞
|
||||||
|
*
|
||||||
|
* @param resourceId 资源ID
|
||||||
|
* @return true-已点赞, false-已取消
|
||||||
|
*/
|
||||||
|
boolean toggleAiResourceLike(Long resourceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加AI资源浏览次数
|
||||||
|
*
|
||||||
|
* @param id 资源ID
|
||||||
|
*/
|
||||||
|
void incrementViewCount(Long id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,241 @@
|
||||||
|
package com.microservices.zone.resource.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.microservices.common.core.constant.SecurityConstants;
|
||||||
|
import com.microservices.common.core.exception.ServiceException;
|
||||||
|
import com.microservices.common.core.utils.StringUtils;
|
||||||
|
import com.microservices.system.api.RemoteFileService;
|
||||||
|
import com.microservices.system.api.domain.SysFile;
|
||||||
|
import com.microservices.system.api.utils.FeignUtils;
|
||||||
|
import com.microservices.zone.resource.domain.vo.AiSkillParseResultVo;
|
||||||
|
import com.microservices.zone.resource.service.IAiSkillParseService;
|
||||||
|
import com.microservices.zone.utils.ZoneConstants;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skill ZIP解析服务实现
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class AiSkillParseServiceImpl implements IAiSkillParseService {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AiSkillParseServiceImpl.class);
|
||||||
|
private static final String SKILL_MD_FILE = "SKILL.md";
|
||||||
|
|
||||||
|
private final RemoteFileService remoteFileService;
|
||||||
|
|
||||||
|
public AiSkillParseServiceImpl(RemoteFileService remoteFileService) {
|
||||||
|
this.remoteFileService = remoteFileService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AiSkillParseResultVo parseSkillZip(MultipartFile zipFile) {
|
||||||
|
// 1. 校验文件大小
|
||||||
|
if (zipFile.getSize() > ZoneConstants.SKILL_ZIP_MAX_SIZE) {
|
||||||
|
throw new ServiceException("ZIP包大小不能超过10MB");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 校验文件格式
|
||||||
|
String originalFilename = zipFile.getOriginalFilename();
|
||||||
|
if (originalFilename == null || !originalFilename.toLowerCase().endsWith(".zip")) {
|
||||||
|
throw new ServiceException("仅支持ZIP格式文件");
|
||||||
|
}
|
||||||
|
|
||||||
|
try (ZipInputStream zis = new ZipInputStream(zipFile.getInputStream(), StandardCharsets.UTF_8)) {
|
||||||
|
ZipEntry entry;
|
||||||
|
String skillMdContent = null;
|
||||||
|
List<FileTreeNode> fileTreeNodes = new ArrayList<>();
|
||||||
|
|
||||||
|
while ((entry = zis.getNextEntry()) != null) {
|
||||||
|
String entryName = entry.getName();
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 防止路径遍历攻击
|
||||||
|
if (entryName.contains("..")) {
|
||||||
|
throw new ServiceException("ZIP包中包含非法路径");
|
||||||
|
}
|
||||||
|
// 提取 SKILL.md(在任意层级下查找)
|
||||||
|
String fileName = new File(entryName).getName();
|
||||||
|
if (SKILL_MD_FILE.equalsIgnoreCase(fileName)) {
|
||||||
|
skillMdContent = readEntryContent(zis);
|
||||||
|
}
|
||||||
|
// 收集文件信息构建目录树
|
||||||
|
fileTreeNodes.add(new FileTreeNode(entryName, entry.getSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 校验 SKILL.md 存在性
|
||||||
|
if (skillMdContent == null) {
|
||||||
|
throw new ServiceException("ZIP包中必须包含 SKILL.md 文件");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 校验 SKILL.md 内容长度
|
||||||
|
if (skillMdContent.trim().length() < ZoneConstants.SKILL_MD_MIN_LENGTH) {
|
||||||
|
throw new ServiceException("SKILL.md 内容不得少于" + ZoneConstants.SKILL_MD_MIN_LENGTH + "字");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 构建目录树 JSON
|
||||||
|
JSONObject fileTreeJson = buildFileTreeJson(fileTreeNodes);
|
||||||
|
|
||||||
|
AiSkillParseResultVo result = new AiSkillParseResultVo();
|
||||||
|
result.setSkillMdContent(skillMdContent);
|
||||||
|
result.setFileTreeJson(fileTreeJson.toJSONString());
|
||||||
|
|
||||||
|
// 从 YAML front matter 中提取 description 字段
|
||||||
|
String description = extractDescription(skillMdContent);
|
||||||
|
result.setSummary(description);
|
||||||
|
|
||||||
|
// 解析成功后,自动上传ZIP文件到File微服务(上传失败则阻断整个接口)
|
||||||
|
SysFile sysFile = FeignUtils.getReturnData(
|
||||||
|
remoteFileService.upload(zipFile, "ai-resource", "skill", SecurityConstants.INNER)
|
||||||
|
);
|
||||||
|
if (sysFile == null || StringUtils.isEmpty(sysFile.getFileIdentifier())) {
|
||||||
|
throw new ServiceException("文件上传返回为空,请重试");
|
||||||
|
}
|
||||||
|
result.setFileId(sysFile.getFileId());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (ServiceException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("ZIP包解析失败", e);
|
||||||
|
throw new ServiceException("ZIP包解析失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readEntryContent(ZipInputStream zis) throws IOException {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int len;
|
||||||
|
while ((len = zis.read(buffer)) > 0) {
|
||||||
|
sb.append(new String(buffer, 0, len, StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject buildFileTreeJson(List<FileTreeNode> nodes) {
|
||||||
|
// 构建树形结构的目录
|
||||||
|
JSONObject root = new JSONObject();
|
||||||
|
root.put("name", "root");
|
||||||
|
root.put("type", "folder");
|
||||||
|
JSONArray rootChildren = new JSONArray();
|
||||||
|
root.put("children", rootChildren);
|
||||||
|
|
||||||
|
for (FileTreeNode node : nodes) {
|
||||||
|
String[] parts = node.path.split("/");
|
||||||
|
JSONArray currentChildren = rootChildren;
|
||||||
|
|
||||||
|
for (int i = 0; i < parts.length; i++) {
|
||||||
|
String part = parts[i];
|
||||||
|
if (StringUtils.isEmpty(part)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean isFile = (i == parts.length - 1);
|
||||||
|
|
||||||
|
if (isFile) {
|
||||||
|
JSONObject fileNode = new JSONObject();
|
||||||
|
fileNode.put("name", part);
|
||||||
|
fileNode.put("type", "file");
|
||||||
|
fileNode.put("size", node.size);
|
||||||
|
currentChildren.add(fileNode);
|
||||||
|
} else {
|
||||||
|
// 查找是否已存在该文件夹
|
||||||
|
JSONObject existingFolder = null;
|
||||||
|
for (int j = 0; j < currentChildren.size(); j++) {
|
||||||
|
JSONObject child = currentChildren.getJSONObject(j);
|
||||||
|
if (part.equals(child.get("name")) && "folder".equals(child.get("type"))) {
|
||||||
|
existingFolder = child;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingFolder == null) {
|
||||||
|
existingFolder = new JSONObject();
|
||||||
|
existingFolder.put("name", part);
|
||||||
|
existingFolder.put("type", "folder");
|
||||||
|
existingFolder.put("children", new ArrayList<JSONObject>());
|
||||||
|
currentChildren.add(existingFolder);
|
||||||
|
}
|
||||||
|
currentChildren = existingFolder.getJSONArray("children");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果根目录只有一个子文件夹,直接返回该子文件夹
|
||||||
|
JSONArray children = root.getJSONArray("children");
|
||||||
|
if (children.size() == 1 && "folder".equals(children.getJSONObject(0).get("type"))) {
|
||||||
|
return children.getJSONObject(0);
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FileTreeNode {
|
||||||
|
final String path;
|
||||||
|
final long size;
|
||||||
|
|
||||||
|
FileTreeNode(String path, long size) {
|
||||||
|
this.path = path;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 SKILL.md 内容中提取 description 字段
|
||||||
|
*
|
||||||
|
* @param skillMdContent SKILL.md 文件内容
|
||||||
|
* @return description 内容,如果不存在则返回 null
|
||||||
|
*/
|
||||||
|
private String extractDescription(String skillMdContent) {
|
||||||
|
if (StringUtils.isEmpty(skillMdContent)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找 YAML front matter 边界
|
||||||
|
String[] lines = skillMdContent.split("\\n");
|
||||||
|
boolean inFrontMatter = false;
|
||||||
|
boolean foundStart = false;
|
||||||
|
|
||||||
|
for (String line : lines) {
|
||||||
|
String trimmed = line.trim();
|
||||||
|
|
||||||
|
// 检测 front matter 开始
|
||||||
|
if (!foundStart && trimmed.equals("---")) {
|
||||||
|
foundStart = true;
|
||||||
|
inFrontMatter = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测 front matter 结束
|
||||||
|
if (foundStart && trimmed.equals("---")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在 front matter 中查找 description
|
||||||
|
if (inFrontMatter && trimmed.startsWith("description:")) {
|
||||||
|
String desc = trimmed.substring("description:".length()).trim();
|
||||||
|
// 处理引号包裹的情况
|
||||||
|
if (desc.startsWith("\"") && desc.endsWith("\"")) {
|
||||||
|
return desc.substring(1, desc.length() - 1);
|
||||||
|
}
|
||||||
|
if (desc.startsWith("'") && desc.endsWith("'")) {
|
||||||
|
return desc.substring(1, desc.length() - 1);
|
||||||
|
}
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,9 +24,12 @@ import com.microservices.zone.detail.service.IZoneDetailService;
|
||||||
import com.microservices.zone.resource.domain.ZoneResource;
|
import com.microservices.zone.resource.domain.ZoneResource;
|
||||||
import com.microservices.zone.resource.domain.ZoneResourceToType;
|
import com.microservices.zone.resource.domain.ZoneResourceToType;
|
||||||
import com.microservices.zone.resource.domain.ZoneResourceType;
|
import com.microservices.zone.resource.domain.ZoneResourceType;
|
||||||
|
import com.microservices.zone.resource.domain.AiResourceLike;
|
||||||
import com.microservices.zone.resource.domain.vo.*;
|
import com.microservices.zone.resource.domain.vo.*;
|
||||||
|
import com.microservices.zone.resource.enums.AiResourceType;
|
||||||
import com.microservices.zone.resource.mapper.ZoneResourceMapper;
|
import com.microservices.zone.resource.mapper.ZoneResourceMapper;
|
||||||
import com.microservices.zone.resource.mapper.ZoneResourceToTypeMapper;
|
import com.microservices.zone.resource.mapper.ZoneResourceToTypeMapper;
|
||||||
|
import com.microservices.zone.resource.mapper.AiResourceLikeMapper;
|
||||||
import com.microservices.zone.resource.service.IZoneResourceDomainService;
|
import com.microservices.zone.resource.service.IZoneResourceDomainService;
|
||||||
import com.microservices.zone.resource.service.IZoneResourceService;
|
import com.microservices.zone.resource.service.IZoneResourceService;
|
||||||
import com.microservices.zone.resource.service.IZoneResourceTypeService;
|
import com.microservices.zone.resource.service.IZoneResourceTypeService;
|
||||||
|
|
@ -37,6 +40,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -72,6 +76,8 @@ public class ZoneResourceServiceImpl implements IZoneResourceService {
|
||||||
@Autowired
|
@Autowired
|
||||||
@Lazy
|
@Lazy
|
||||||
private IZoneSpecialProjectRelevancyService zoneSpecialProjectRelevancyService;
|
private IZoneSpecialProjectRelevancyService zoneSpecialProjectRelevancyService;
|
||||||
|
@Autowired
|
||||||
|
private AiResourceLikeMapper aiResourceLikeMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询特色专区资源聚合
|
* 查询特色专区资源聚合
|
||||||
|
|
@ -155,9 +161,7 @@ public class ZoneResourceServiceImpl implements IZoneResourceService {
|
||||||
if (1 == zoneDetail.getResourceNeedAudit()) {
|
if (1 == zoneDetail.getResourceNeedAudit()) {
|
||||||
boolean isOperator = AuthUtil.hasUserIdentity(zoneDetail.getDeptId(), SystemRole.ZONE_OPERATOR.getRoleKey());
|
boolean isOperator = AuthUtil.hasUserIdentity(zoneDetail.getDeptId(), SystemRole.ZONE_OPERATOR.getRoleKey());
|
||||||
boolean isMember = AuthUtil.hasUserIdentity(zoneDetail.getDeptId(), SystemRole.ZONE_MEMBER.getRoleKey());
|
boolean isMember = AuthUtil.hasUserIdentity(zoneDetail.getDeptId(), SystemRole.ZONE_MEMBER.getRoleKey());
|
||||||
if (isOperator || isMember) {
|
return isOperator || isMember;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -224,7 +228,7 @@ public class ZoneResourceServiceImpl implements IZoneResourceService {
|
||||||
);
|
);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("从文件微服务获取资源附件时失败:{}", e.getMessage());
|
logger.error("从文件微服务获取资源附件时失败:{}", e.getMessage());
|
||||||
throw new ServiceException("资源附件列表异常:"+e.getMessage());
|
throw new ServiceException("资源附件列表异常:" + e.getMessage());
|
||||||
}
|
}
|
||||||
return sysFileInfoList;
|
return sysFileInfoList;
|
||||||
}
|
}
|
||||||
|
|
@ -410,4 +414,172 @@ public class ZoneResourceServiceImpl implements IZoneResourceService {
|
||||||
ZoneResource zoneResource = selectZoneResourceById(id);
|
ZoneResource zoneResource = selectZoneResourceById(id);
|
||||||
return zoneResourceToData(zoneResource);
|
return zoneResourceToData(zoneResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== AI资源广场 Skill 功能 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZoneResourceDataVo insertAiSkillResource(AiSkillInputVo inputVo) {
|
||||||
|
ZoneResourceInputVo zoneResourceInputVo = inputVo.toZoneResourceInputVo();
|
||||||
|
return insertZoneResource(zoneResourceInputVo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZoneResourceDataVo updateAiSkillResource(AiSkillUpdateVo updateVo) {
|
||||||
|
ZoneResource oldResource = getAiResourceById(updateVo.getId());
|
||||||
|
if (!AiResourceType.SKILL.getCode().equals(oldResource.getResourceCategory())) {
|
||||||
|
throw new ServiceException("当前资源非Skill资源");
|
||||||
|
}
|
||||||
|
// 已发布状态不允许直接修改
|
||||||
|
if (ZoneConstants.RESOURCE_AUDIT_PASS.equals(oldResource.getAuditStatus())) {
|
||||||
|
throw new ServiceException("已发布的资源不允许直接修改,请先下架后再修改");
|
||||||
|
}
|
||||||
|
ZoneResourceUpdateVo zoneResourceUpdateVo = updateVo.toZoneResourceUpdateVo(oldResource);
|
||||||
|
return updateZoneResource(zoneResourceUpdateVo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZoneResourceDataVo selectAiSkillResourceById(Long id) {
|
||||||
|
ZoneResource resource = getAiResourceById(id);
|
||||||
|
return zoneResourceToData(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GenericsTableDataInfo<ZoneResourceDataVo> selectAiSkillResourceList(ZoneResource zoneResource) {
|
||||||
|
zoneResource.setResourceCategory(AiResourceType.SKILL.getCode());
|
||||||
|
List<ZoneResource> list = zoneResourceMapper.selectZoneResourceList(zoneResource);
|
||||||
|
return PageUtils.toPage(list, this::zoneResourceToData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int takeDownAiResource(Long id, String reason) {
|
||||||
|
ZoneResource resource = getAiResourceById(id);
|
||||||
|
|
||||||
|
if (!ZoneConstants.RESOURCE_AUDIT_PASS.equals(resource.getAuditStatus())) {
|
||||||
|
throw new ServiceException("仅已发布的资源可以下架");
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.setAuditStatus(ZoneConstants.RESOURCE_TAKEN_DOWN);
|
||||||
|
resource.setRemark(reason);
|
||||||
|
resource.setUpdateTime(DateUtils.getNowDate());
|
||||||
|
resource.setUpdateBy(SecurityUtils.getUsername());
|
||||||
|
return zoneResourceMapper.updateZoneResource(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int resubmitAiResource(Long id) {
|
||||||
|
ZoneResource resource = getAiResourceById(id);
|
||||||
|
checkResourceOwner(resource);
|
||||||
|
|
||||||
|
if (!ZoneConstants.RESOURCE_AUDIT_FAILED.equals(resource.getAuditStatus())
|
||||||
|
&& !ZoneConstants.RESOURCE_TAKEN_DOWN.equals(resource.getAuditStatus())) {
|
||||||
|
throw new ServiceException("仅被驳回或已下架的资源可以重新提交");
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.setAuditStatus(ZoneConstants.RESOURCE_NOT_AUDIT);
|
||||||
|
resource.setRemark(null);
|
||||||
|
resource.setUpdateTime(DateUtils.getNowDate());
|
||||||
|
return zoneResourceMapper.updateZoneResource(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int withdrawAiResource(Long id) {
|
||||||
|
ZoneResource resource = getAiResourceById(id);
|
||||||
|
checkResourceOwner(resource);
|
||||||
|
|
||||||
|
if (!ZoneConstants.RESOURCE_NOT_AUDIT.equals(resource.getAuditStatus())) {
|
||||||
|
throw new ServiceException("仅待审核的资源可以撤回");
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.setAuditStatus(ZoneConstants.RESOURCE_AUDIT_FAILED);
|
||||||
|
resource.setUpdateTime(DateUtils.getNowDate());
|
||||||
|
return zoneResourceMapper.updateZoneResource(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ZoneResourceDataVo> selectSkillRecommendations(int limit) {
|
||||||
|
List<ZoneResource> list = zoneResourceMapper.selectSkillRecommendations(
|
||||||
|
AiResourceType.SKILL.getCode(), ZoneConstants.RESOURCE_AUDIT_PASS, limit);
|
||||||
|
return list.stream().map(this::zoneResourceToData).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean toggleAiResourceLike(Long resourceId) {
|
||||||
|
// 校验资源存在且已发布
|
||||||
|
ZoneResource resource = getAiResourceById(resourceId);
|
||||||
|
if (!ZoneConstants.RESOURCE_AUDIT_PASS.equals(resource.getAuditStatus())) {
|
||||||
|
throw new ServiceException("仅已发布的资源可以点赞");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long userId = SecurityUtils.getGitlinkUserId();
|
||||||
|
if (userId == null) {
|
||||||
|
throw new ServiceException("当前用户未绑定平台账号,无法点赞");
|
||||||
|
}
|
||||||
|
|
||||||
|
AiResourceLike existingLike = aiResourceLikeMapper.selectByResourceAndUser(resourceId, userId);
|
||||||
|
if (existingLike != null) {
|
||||||
|
// 取消点赞
|
||||||
|
aiResourceLikeMapper.deleteLike(resourceId, userId);
|
||||||
|
zoneResourceMapper.updateLikeCount(resourceId, -1);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// 新增点赞
|
||||||
|
AiResourceLike like = new AiResourceLike();
|
||||||
|
like.setResourceId(resourceId);
|
||||||
|
like.setUserId(userId);
|
||||||
|
like.setCreateTime(DateUtils.getNowDate());
|
||||||
|
aiResourceLikeMapper.insertLike(like);
|
||||||
|
zoneResourceMapper.updateLikeCount(resourceId, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementViewCount(Long id) {
|
||||||
|
zoneResourceMapper.incrementViewCount(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== AI资源辅助方法 ====================
|
||||||
|
|
||||||
|
private ZoneResource getAiResourceById(Long id) {
|
||||||
|
ZoneResource resource = zoneResourceMapper.selectZoneResourceById(id);
|
||||||
|
if (resource == null) {
|
||||||
|
throw new ServiceException("资源不存在(资源id[" + id + "])");
|
||||||
|
}
|
||||||
|
if (!AiResourceType.SKILL.getCode().equals(resource.getResourceCategory())) {
|
||||||
|
throw new ServiceException("该资源不是AI资源");
|
||||||
|
}
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkResourceOwner(ZoneResource resource) {
|
||||||
|
if (!resource.getCreateBy().equals(SecurityUtils.getUsername())) {
|
||||||
|
throw new ServiceException("仅允许操作自己发布的资源");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateSkillNameUnique(String name, Long excludeId) {
|
||||||
|
if (StringUtils.isEmpty(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ZoneResource existing = zoneResourceMapper.selectSkillResourceByName(name);
|
||||||
|
if (existing != null && !existing.getId().equals(excludeId)) {
|
||||||
|
throw new ServiceException("Skill名称已存在(名称[" + name + "])");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateKeywords(String keywords) {
|
||||||
|
if (StringUtils.isEmpty(keywords)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String[] tags = keywords.split(",");
|
||||||
|
if (tags.length > ZoneConstants.KEYWORD_MAX_COUNT) {
|
||||||
|
throw new ServiceException("标签数量不能超过" + ZoneConstants.KEYWORD_MAX_COUNT + "个");
|
||||||
|
}
|
||||||
|
for (String tag : tags) {
|
||||||
|
if (tag.trim().length() > ZoneConstants.KEYWORD_MAX_LENGTH) {
|
||||||
|
throw new ServiceException("单个标签不能超过" + ZoneConstants.KEYWORD_MAX_LENGTH + "个字符");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package com.microservices.zone.resource.utils;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.microservices.common.core.utils.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源 extendData 工具类
|
||||||
|
* 用于处理不同资源类型的扩展数据
|
||||||
|
*/
|
||||||
|
public class ResourceExtendDataUtils {
|
||||||
|
|
||||||
|
private static final String SKILL_ROOT = "skill";
|
||||||
|
private static final String SKILL_CONTENT = "content";
|
||||||
|
private static final String SKILL_FILE_TREE = "fileTree";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建 Skill 资源的 extendData JSONObject
|
||||||
|
*
|
||||||
|
* @param skillContent SKILL.md 内容
|
||||||
|
* @param fileTreeJson 目录树 JSONObject
|
||||||
|
* @return extendData JSONObject
|
||||||
|
*/
|
||||||
|
public static JSONObject buildSkillExtendData(String skillContent, JSONObject fileTreeJson) {
|
||||||
|
JSONObject extendData = new JSONObject();
|
||||||
|
JSONObject skill = new JSONObject();
|
||||||
|
skill.put(SKILL_CONTENT, skillContent);
|
||||||
|
if (fileTreeJson != null) {
|
||||||
|
skill.put(SKILL_FILE_TREE, fileTreeJson);
|
||||||
|
}
|
||||||
|
extendData.put(SKILL_ROOT, skill);
|
||||||
|
return extendData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 extendData 中提取 skillContent
|
||||||
|
*
|
||||||
|
* @param extendData extendData JSONObject
|
||||||
|
* @return skillContent,不存在返回 null
|
||||||
|
*/
|
||||||
|
public static String extractSkillContent(JSONObject extendData) {
|
||||||
|
if (extendData == null || !extendData.containsKey(SKILL_ROOT)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JSONObject skill = extendData.getJSONObject(SKILL_ROOT);
|
||||||
|
return skill.getString(SKILL_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 extendData 中提取 fileTree
|
||||||
|
*
|
||||||
|
* @param extendData extendData JSONObject
|
||||||
|
* @return fileTree JSONObject,不存在返回 null
|
||||||
|
*/
|
||||||
|
public static JSONObject extractSkillFileTree(JSONObject extendData) {
|
||||||
|
if (extendData == null || !extendData.containsKey(SKILL_ROOT)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JSONObject skill = extendData.getJSONObject(SKILL_ROOT);
|
||||||
|
return skill.getJSONObject(SKILL_FILE_TREE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新 extendData 中的 skillContent
|
||||||
|
*
|
||||||
|
* @param extendData extendData JSONObject
|
||||||
|
* @param skillContent 新的 skillContent
|
||||||
|
* @return 更新后的 extendData
|
||||||
|
*/
|
||||||
|
public static JSONObject updateSkillContent(JSONObject extendData, String skillContent) {
|
||||||
|
if (extendData == null) {
|
||||||
|
extendData = new JSONObject();
|
||||||
|
}
|
||||||
|
if (!extendData.containsKey(SKILL_ROOT)) {
|
||||||
|
extendData.put(SKILL_ROOT, new JSONObject());
|
||||||
|
}
|
||||||
|
JSONObject skill = extendData.getJSONObject(SKILL_ROOT);
|
||||||
|
skill.put(SKILL_CONTENT, skillContent);
|
||||||
|
return extendData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新 extendData 中的 fileTree
|
||||||
|
*
|
||||||
|
* @param extendData extendData JSONObject
|
||||||
|
* @param fileTreeJson 新的 fileTree
|
||||||
|
* @return 更新后的 extendData
|
||||||
|
*/
|
||||||
|
public static JSONObject updateSkillFileTree(JSONObject extendData, JSONObject fileTreeJson) {
|
||||||
|
if (extendData == null) {
|
||||||
|
extendData = new JSONObject();
|
||||||
|
}
|
||||||
|
if (!extendData.containsKey(SKILL_ROOT)) {
|
||||||
|
extendData.put(SKILL_ROOT, new JSONObject());
|
||||||
|
}
|
||||||
|
JSONObject skill = extendData.getJSONObject(SKILL_ROOT);
|
||||||
|
skill.put(SKILL_FILE_TREE, fileTreeJson);
|
||||||
|
return extendData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -66,6 +66,10 @@ public class ZoneConstants {
|
||||||
* 资源审核状态-待审核
|
* 资源审核状态-待审核
|
||||||
*/
|
*/
|
||||||
public final static String RESOURCE_NOT_AUDIT = "0";
|
public final static String RESOURCE_NOT_AUDIT = "0";
|
||||||
|
/**
|
||||||
|
* 资源审核状态-已下架
|
||||||
|
*/
|
||||||
|
public final static String RESOURCE_TAKEN_DOWN = "3";
|
||||||
/**
|
/**
|
||||||
* 前台权限-管理者
|
* 前台权限-管理者
|
||||||
*/
|
*/
|
||||||
|
|
@ -82,4 +86,24 @@ public class ZoneConstants {
|
||||||
public static String getFileSuffixOfResourceTypeRedisKey(Long zoneId, String fileSuffix) {
|
public static String getFileSuffixOfResourceTypeRedisKey(Long zoneId, String fileSuffix) {
|
||||||
return REDIS_KEY_FILE_SUFFIX_OF_RESOURCE_TYPE_PREFIX + zoneId + "_" + fileSuffix;
|
return REDIS_KEY_FILE_SUFFIX_OF_RESOURCE_TYPE_PREFIX + zoneId + "_" + fileSuffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skill ZIP文件最大大小(10MB)
|
||||||
|
*/
|
||||||
|
public final static long SKILL_ZIP_MAX_SIZE = 10 * 1024 * 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SKILL.md 最少内容长度
|
||||||
|
*/
|
||||||
|
public final static int SKILL_MD_MIN_LENGTH = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签最大数量
|
||||||
|
*/
|
||||||
|
public final static int KEYWORD_MAX_COUNT = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单个标签最大字符数
|
||||||
|
*/
|
||||||
|
public final static int KEYWORD_MAX_LENGTH = 10;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?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.resource.mapper.AiResourceLikeMapper">
|
||||||
|
|
||||||
|
<select id="selectByResourceAndUser" resultType="com.microservices.zone.resource.domain.AiResourceLike">
|
||||||
|
select id, resource_id, user_id, create_time
|
||||||
|
from ai_resource_like
|
||||||
|
where resource_id = #{resourceId} and user_id = #{userId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertLike" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
insert into ai_resource_like (resource_id, user_id, create_time)
|
||||||
|
values (#{resourceId}, #{userId}, #{createTime})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<delete id="deleteLike">
|
||||||
|
delete from ai_resource_like
|
||||||
|
where resource_id = #{resourceId} and user_id = #{userId}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -26,7 +26,11 @@
|
||||||
<result property="summary" column="summary"/>
|
<result property="summary" column="summary"/>
|
||||||
<result property="remark" column="remark"/>
|
<result property="remark" column="remark"/>
|
||||||
<result property="keywords" column="keywords"/>
|
<result property="keywords" column="keywords"/>
|
||||||
<result property="extendData" column="extend_data"/>
|
<result property="extendData" column="extend_data"
|
||||||
|
typeHandler="com.microservices.zone.resource.handler.JsonObjectTypeHandler"/>
|
||||||
|
<result property="resourceCategory" column="resource_category"/>
|
||||||
|
<result property="viewCount" column="view_count"/>
|
||||||
|
<result property="likeCount" column="like_count"/>
|
||||||
<association property="zoneResourceDomain" column="domain_id"
|
<association property="zoneResourceDomain" column="domain_id"
|
||||||
javaType="com.microservices.zone.resource.domain.ZoneResourceDomain"
|
javaType="com.microservices.zone.resource.domain.ZoneResourceDomain"
|
||||||
resultMap="domainResult"/>
|
resultMap="domainResult"/>
|
||||||
|
|
@ -59,7 +63,10 @@
|
||||||
remark,
|
remark,
|
||||||
update_time,
|
update_time,
|
||||||
keywords,
|
keywords,
|
||||||
extend_data
|
extend_data,
|
||||||
|
resource_category,
|
||||||
|
view_count,
|
||||||
|
like_count
|
||||||
from zone_resource
|
from zone_resource
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
|
|
@ -85,6 +92,9 @@
|
||||||
zr.remark,
|
zr.remark,
|
||||||
zr.keywords,
|
zr.keywords,
|
||||||
zr.extend_data,
|
zr.extend_data,
|
||||||
|
zr.resource_category,
|
||||||
|
zr.view_count,
|
||||||
|
zr.like_count,
|
||||||
zrd.name as domain_name
|
zrd.name as domain_name
|
||||||
from zone_resource zr
|
from zone_resource zr
|
||||||
left join zone_resource_domain zrd on zr.domain_id = zrd.id
|
left join zone_resource_domain zrd on zr.domain_id = zrd.id
|
||||||
|
|
@ -113,6 +123,9 @@
|
||||||
zr.remark,
|
zr.remark,
|
||||||
zr.keywords,
|
zr.keywords,
|
||||||
zr.extend_data,
|
zr.extend_data,
|
||||||
|
zr.resource_category,
|
||||||
|
zr.view_count,
|
||||||
|
zr.like_count,
|
||||||
zrd.name as domain_name
|
zrd.name as domain_name
|
||||||
from zone_resource zr
|
from zone_resource zr
|
||||||
left join zone_resource_domain zrd on zr.domain_id = zrd.id
|
left join zone_resource_domain zrd on zr.domain_id = zrd.id
|
||||||
|
|
@ -127,6 +140,7 @@
|
||||||
<if test="deptId != null ">and zr.dept_id = #{deptId}</if>
|
<if test="deptId != null ">and zr.dept_id = #{deptId}</if>
|
||||||
<if test="createBy != null and createBy != ''">and zr.create_by = #{createBy}</if>
|
<if test="createBy != null and createBy != ''">and zr.create_by = #{createBy}</if>
|
||||||
<if test="keywords != null and keywords != ''">and zr.keywords like concat('%', #{keywords}, '%')</if>
|
<if test="keywords != null and keywords != ''">and zr.keywords like concat('%', #{keywords}, '%')</if>
|
||||||
|
<if test="resourceCategory != null and resourceCategory != ''">and zr.resource_category = #{resourceCategory}</if>
|
||||||
<choose>
|
<choose>
|
||||||
<when test="auditStatus != null and auditStatus !=''">
|
<when test="auditStatus != null and auditStatus !=''">
|
||||||
and zr.audit_status = #{auditStatus}
|
and zr.audit_status = #{auditStatus}
|
||||||
|
|
@ -231,6 +245,9 @@
|
||||||
<if test="remark != null">remark,</if>
|
<if test="remark != null">remark,</if>
|
||||||
<if test="keywords != null">keywords,</if>
|
<if test="keywords != null">keywords,</if>
|
||||||
<if test="extendData != null">extend_data,</if>
|
<if test="extendData != null">extend_data,</if>
|
||||||
|
<if test="resourceCategory != null">resource_category,</if>
|
||||||
|
<if test="viewCount != null">view_count,</if>
|
||||||
|
<if test="likeCount != null">like_count,</if>
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="name != null">#{name},</if>
|
<if test="name != null">#{name},</if>
|
||||||
|
|
@ -253,7 +270,10 @@
|
||||||
<if test="auditBy != null">#{auditBy},</if>
|
<if test="auditBy != null">#{auditBy},</if>
|
||||||
<if test="remark != null">#{remark},</if>
|
<if test="remark != null">#{remark},</if>
|
||||||
<if test="keywords != null">#{keywords},</if>
|
<if test="keywords != null">#{keywords},</if>
|
||||||
<if test="extendData != null">#{extendData},</if>
|
<if test="extendData != null">#{extendData, typeHandler=com.microservices.zone.resource.handler.JsonObjectTypeHandler},</if>
|
||||||
|
<if test="resourceCategory != null">#{resourceCategory},</if>
|
||||||
|
<if test="viewCount != null">#{viewCount},</if>
|
||||||
|
<if test="likeCount != null">#{likeCount},</if>
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
@ -279,7 +299,10 @@
|
||||||
<if test="auditTime != null">audit_time = #{auditTime},</if>
|
<if test="auditTime != null">audit_time = #{auditTime},</if>
|
||||||
<if test="auditBy != null">audit_by = #{auditBy},</if>
|
<if test="auditBy != null">audit_by = #{auditBy},</if>
|
||||||
<if test="keywords != null">keywords = #{keywords},</if>
|
<if test="keywords != null">keywords = #{keywords},</if>
|
||||||
<if test="extendData != null">extend_data = #{extendData},</if>
|
<if test="extendData != null">extend_data = #{extendData, typeHandler=com.microservices.zone.resource.handler.JsonObjectTypeHandler},</if>
|
||||||
|
<if test="resourceCategory != null">resource_category = #{resourceCategory},</if>
|
||||||
|
<if test="viewCount != null">view_count = #{viewCount},</if>
|
||||||
|
<if test="likeCount != null">like_count = #{likeCount},</if>
|
||||||
</trim>
|
</trim>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
@ -301,4 +324,27 @@
|
||||||
from zone_resource
|
from zone_resource
|
||||||
where zone_id = #{zoneId}
|
where zone_id = #{zoneId}
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
|
<select id="selectSkillResourceByName" resultType="com.microservices.zone.resource.domain.ZoneResource">
|
||||||
|
<include refid="selectZoneResourceVo"/>
|
||||||
|
where name = #{name} and resource_category = 'SKILL' and del_flag = '0'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectSkillRecommendations" resultMap="ZoneResourceResult">
|
||||||
|
<include refid="selectZoneResourceAliasVo"/>
|
||||||
|
where zr.resource_category = #{resourceCategory}
|
||||||
|
and zr.audit_status = #{auditStatus}
|
||||||
|
and zr.del_flag = '0'
|
||||||
|
order by (select sum(download_count) from sys_file_info sf where FIND_IN_SET(sf.file_id,zr.file_ids)) desc,
|
||||||
|
zr.like_count desc, zr.view_count desc
|
||||||
|
limit #{limit}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<update id="incrementViewCount">
|
||||||
|
update zone_resource set view_count = IFNULL(view_count, 0) + 1 where id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<update id="updateLikeCount">
|
||||||
|
update zone_resource set like_count = IFNULL(like_count, 0) + #{delta} where id = #{id}
|
||||||
|
</update>
|
||||||
</mapper>
|
</mapper>
|
||||||
Loading…
Reference in New Issue