feat: expand EasyCode software factory workflows
This commit is contained in:
@@ -3,6 +3,7 @@ package com.ruoyi.web.controller.front;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -13,6 +14,8 @@ import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.framework.web.service.TokenService;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontLoginBody;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontPasswordUpdateBody;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontProfileUpdateBody;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontRegisterBody;
|
||||
import com.ruoyi.generator.service.front.IFrontUserService;
|
||||
import com.ruoyi.web.service.front.FrontAuthService;
|
||||
@@ -53,6 +56,18 @@ public class FrontAuthController extends BaseController
|
||||
return AjaxResult.success(frontAuthService.profile(SecurityUtils.getUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/profile")
|
||||
public AjaxResult updateProfile(@RequestBody FrontProfileUpdateBody body)
|
||||
{
|
||||
return AjaxResult.success(frontAuthService.updateProfile(SecurityUtils.getUserId(), body));
|
||||
}
|
||||
|
||||
@PutMapping("/password")
|
||||
public AjaxResult updatePassword(@RequestBody FrontPasswordUpdateBody body)
|
||||
{
|
||||
return toAjax(frontAuthService.updatePassword(SecurityUtils.getUserId(), body));
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public AjaxResult logout()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.ruoyi.web.controller.front;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.generator.factory.dsl.ProjectSpecQueryService;
|
||||
import com.ruoyi.generator.factory.dsl.model.ProjectSpecV1;
|
||||
import com.ruoyi.generator.factory.diff.ProjectSpecVersionDiffService;
|
||||
import com.ruoyi.generator.factory.generation.ProjectSpecGenerationService;
|
||||
import com.ruoyi.generator.factory.generation.ProjectSpecGenerationRunService;
|
||||
import com.ruoyi.generator.factory.patch.ProjectSpecPatchService;
|
||||
import com.ruoyi.generator.factory.patch.model.ProjectSpecPatchRequest;
|
||||
import com.ruoyi.generator.factory.validation.ProjectSpecValidationService;
|
||||
import com.ruoyi.generator.factory.version.ProjectSpecVersionService;
|
||||
import com.ruoyi.generator.factory.version.model.ProjectSpecRollbackRequest;
|
||||
import com.ruoyi.generator.factory.version.model.ProjectSpecSnapshotRequest;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/front/factory/projects")
|
||||
public class FrontFactoryProjectController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ProjectSpecQueryService projectSpecQueryService;
|
||||
@Autowired
|
||||
private ProjectSpecVersionService projectSpecVersionService;
|
||||
@Autowired
|
||||
private ProjectSpecValidationService projectSpecValidationService;
|
||||
@Autowired
|
||||
private ProjectSpecPatchService projectSpecPatchService;
|
||||
@Autowired
|
||||
private ProjectSpecVersionDiffService projectSpecVersionDiffService;
|
||||
@Autowired
|
||||
private ProjectSpecGenerationService projectSpecGenerationService;
|
||||
@Autowired
|
||||
private ProjectSpecGenerationRunService projectSpecGenerationRunService;
|
||||
|
||||
@GetMapping("/{projectId}/spec")
|
||||
public AjaxResult getCurrentSpec(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecQueryService.getCurrentSpec(userId, projectId));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/spec/validate")
|
||||
public AjaxResult validateSpec(@PathVariable Long projectId, @RequestBody(required = false) ProjectSpecV1 spec)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecValidationService.validate(userId, projectId, spec));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/spec/patch/preview")
|
||||
public AjaxResult previewSpecPatch(@PathVariable Long projectId, @RequestBody ProjectSpecPatchRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecPatchService.preview(userId, projectId, request));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/spec/patch")
|
||||
public AjaxResult applySpecPatch(@PathVariable Long projectId, @RequestBody ProjectSpecPatchRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecPatchService.apply(userId, projectId, request));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/versions")
|
||||
public AjaxResult snapshotCurrentSpec(@PathVariable Long projectId,
|
||||
@RequestBody(required = false) ProjectSpecSnapshotRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
String summary = request == null ? null : request.getChangeSummary();
|
||||
return AjaxResult.success(projectSpecVersionService.snapshotCurrent(userId, projectId, summary));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/versions")
|
||||
public AjaxResult listSpecVersions(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecVersionService.listVersions(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/versions/{specVersionId}")
|
||||
public AjaxResult getSpecVersion(@PathVariable Long projectId, @PathVariable Long specVersionId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecVersionService.getVersion(userId, projectId, specVersionId));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/versions/{specVersionId}/generate")
|
||||
public AjaxResult generateSpecVersion(@PathVariable Long projectId, @PathVariable Long specVersionId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecGenerationService.generate(userId, projectId, specVersionId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/generation-runs")
|
||||
public AjaxResult listGenerationRuns(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecGenerationRunService.list(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/generation-runs/{generationRunId}")
|
||||
public AjaxResult getGenerationRun(@PathVariable Long projectId, @PathVariable Long generationRunId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecGenerationRunService.get(userId, projectId, generationRunId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/generation-runs/{leftRunId}/diff/{rightRunId}")
|
||||
public AjaxResult diffGenerationRuns(@PathVariable Long projectId, @PathVariable Long leftRunId,
|
||||
@PathVariable Long rightRunId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecGenerationRunService.diff(userId, projectId,
|
||||
leftRunId, rightRunId));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/versions/{specVersionId}/rollback")
|
||||
public AjaxResult rollbackSpecVersion(@PathVariable Long projectId, @PathVariable Long specVersionId,
|
||||
@RequestBody(required = false) ProjectSpecRollbackRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
String summary = request == null ? null : request.getChangeSummary();
|
||||
return AjaxResult.success(projectSpecVersionService.rollback(userId, projectId, specVersionId, summary));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/versions/{leftVersionId}/diff/{rightVersionId}")
|
||||
public AjaxResult diffSpecVersions(@PathVariable Long projectId, @PathVariable Long leftVersionId,
|
||||
@PathVariable Long rightVersionId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(projectSpecVersionDiffService.diff(userId, projectId,
|
||||
leftVersionId, rightVersionId));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
package com.ruoyi.web.controller.front;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -13,28 +19,60 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.file.FileUtils;
|
||||
import com.ruoyi.generator.domain.front.FrontProject;
|
||||
import com.ruoyi.generator.domain.front.dto.DatabaseDesignResponse;
|
||||
import com.ruoyi.generator.domain.front.FrontProjectDiagram;
|
||||
import com.ruoyi.generator.domain.front.FrontProjectGeneration;
|
||||
import com.ruoyi.generator.domain.front.FrontProjectPaper;
|
||||
import com.ruoyi.generator.domain.front.dto.AiGenerationTaskCreateRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopAuditResult;
|
||||
import com.ruoyi.generator.domain.front.dto.CodeAnalysisResponse;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan;
|
||||
import com.ruoyi.generator.domain.front.dto.DatabaseDesignResponse;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontProjectCreateRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontProjectUpdateRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.GenerateAppBlueprintRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.GenerateBusinessBlueprintRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.GenerateFlowConfigRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.SimpleFlowConfig;
|
||||
import com.ruoyi.generator.domain.front.dto.GenerateCodeAnalysisRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.GenerateDatabaseRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.GenerateDiagramRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.GeneratePaperDraftRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.GeneratePaperOutlineRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.GenerateRequirementRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.ParseErSqlRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontendPageDesignRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.PreviewFileRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.ProjectDeliveryReadiness;
|
||||
import com.ruoyi.generator.factory.quality.ProjectQualityRunService;
|
||||
import com.ruoyi.generator.factory.artifact.CertifiedArtifactService;
|
||||
import com.ruoyi.generator.factory.artifact.model.ProjectCertifiedArtifact;
|
||||
import com.ruoyi.generator.factory.acceptance.FactoryV1AcceptanceService;
|
||||
import com.ruoyi.generator.service.ITemplateBundleService;
|
||||
import com.ruoyi.generator.service.front.IAiGenerateService;
|
||||
import com.ruoyi.generator.service.front.IAiGenerationTaskService;
|
||||
import com.ruoyi.generator.service.front.BusinessBlockRegistryService;
|
||||
import com.ruoyi.generator.service.front.BusinessLoopCoverageValidator;
|
||||
import com.ruoyi.generator.service.front.BusinessRequirementCoverageValidator;
|
||||
import com.ruoyi.generator.service.front.FlowConfigContractService;
|
||||
import com.ruoyi.generator.service.front.FrontProjectAssetService;
|
||||
import com.ruoyi.generator.service.front.FrontendPageDesignService;
|
||||
import com.ruoyi.generator.service.front.IFrontProjectPreviewService;
|
||||
import com.ruoyi.generator.service.front.IFrontProjectRunPreviewService;
|
||||
import com.ruoyi.generator.service.front.IFrontProjectService;
|
||||
import com.ruoyi.generator.service.front.IProjectDeliveryReadinessService;
|
||||
import com.ruoyi.generator.service.front.SqlDdlParseService;
|
||||
import com.ruoyi.generator.factory.flow.FlowConfigCompilation;
|
||||
import com.ruoyi.generator.factory.flow.FlowConfigProjector;
|
||||
import com.ruoyi.generator.service.front.PageLayoutTemplateRegistry;
|
||||
import com.ruoyi.generator.service.front.PageStylePresetRegistry;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/front/project")
|
||||
@@ -47,6 +85,14 @@ public class FrontProjectController extends BaseController
|
||||
@Autowired
|
||||
private IAiGenerationTaskService aiGenerationTaskService;
|
||||
@Autowired
|
||||
private BusinessLoopCoverageValidator businessLoopCoverageValidator;
|
||||
@Autowired
|
||||
private BusinessRequirementCoverageValidator businessRequirementCoverageValidator;
|
||||
@Autowired
|
||||
private FlowConfigContractService flowConfigContractService;
|
||||
@Autowired
|
||||
private FrontProjectAssetService frontProjectAssetService;
|
||||
@Autowired
|
||||
private FrontendPageDesignService frontendPageDesignService;
|
||||
@Autowired
|
||||
private BusinessBlockRegistryService businessBlockRegistryService;
|
||||
@@ -54,6 +100,22 @@ public class FrontProjectController extends BaseController
|
||||
private IFrontProjectPreviewService frontProjectPreviewService;
|
||||
@Autowired
|
||||
private IFrontProjectRunPreviewService frontProjectRunPreviewService;
|
||||
@Autowired
|
||||
private SqlDdlParseService sqlDdlParseService;
|
||||
@Autowired
|
||||
private ITemplateBundleService templateBundleService;
|
||||
@Autowired
|
||||
private IProjectDeliveryReadinessService projectDeliveryReadinessService;
|
||||
@Autowired
|
||||
private ProjectQualityRunService projectQualityRunService;
|
||||
@Autowired
|
||||
private CertifiedArtifactService certifiedArtifactService;
|
||||
@Autowired
|
||||
private FactoryV1AcceptanceService factoryV1AcceptanceService;
|
||||
@Autowired
|
||||
private PageStylePresetRegistry pageStylePresetRegistry;
|
||||
@Autowired
|
||||
private PageLayoutTemplateRegistry pageLayoutTemplateRegistry;
|
||||
|
||||
@PostMapping("/create")
|
||||
public AjaxResult create(@RequestBody FrontProjectCreateRequest request)
|
||||
@@ -75,6 +137,31 @@ public class FrontProjectController extends BaseController
|
||||
return AjaxResult.success(frontProjectService.listAvailableModules());
|
||||
}
|
||||
|
||||
@GetMapping("/options/code-templates")
|
||||
public AjaxResult codeTemplates()
|
||||
{
|
||||
return AjaxResult.success(templateBundleService.listEnabledBundles());
|
||||
}
|
||||
|
||||
@GetMapping("/options/page-templates")
|
||||
public AjaxResult pageTemplates()
|
||||
{
|
||||
pageStylePresetRegistry.refreshFromDatabase();
|
||||
pageLayoutTemplateRegistry.refreshFromDatabase();
|
||||
Map<String, Object> options = new LinkedHashMap<String, Object>();
|
||||
options.put("themes", pageStylePresetRegistry.options());
|
||||
options.put("solutions", pageStylePresetRegistry.options());
|
||||
options.put("layouts", pageLayoutTemplateRegistry.options());
|
||||
return AjaxResult.success(options);
|
||||
}
|
||||
|
||||
@PostMapping("/generate-requirement")
|
||||
public AjaxResult generateRequirement(@RequestBody GenerateRequirementRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(aiGenerateService.generateRequirementSuggestion(userId, request));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}")
|
||||
public AjaxResult get(@PathVariable Long projectId)
|
||||
{
|
||||
@@ -110,6 +197,14 @@ public class FrontProjectController extends BaseController
|
||||
return AjaxResult.success(frontProjectService.saveDatabase(userId, projectId, request));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/er/parse-sql")
|
||||
public AjaxResult parseErSql(@PathVariable Long projectId, @RequestBody ParseErSqlRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
frontProjectService.getProject(userId, projectId);
|
||||
return AjaxResult.success(sqlDdlParseService.parse(request == null ? "" : request.getSql()));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/page-designs")
|
||||
public AjaxResult pageDesigns(@PathVariable Long projectId,
|
||||
@RequestParam(defaultValue = "frontend") String scope)
|
||||
@@ -127,6 +222,41 @@ public class FrontProjectController extends BaseController
|
||||
return AjaxResult.success(businessBlockRegistryService.listDefinitions(scope));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/diagrams")
|
||||
public AjaxResult diagrams(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(frontProjectAssetService.listDiagrams(userId, projectId));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/diagrams")
|
||||
public AjaxResult saveDiagram(@PathVariable Long projectId, @RequestBody FrontProjectDiagram request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(frontProjectAssetService.saveDiagram(userId, projectId, request));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{projectId}/diagrams/{diagramId}")
|
||||
public AjaxResult deleteDiagram(@PathVariable Long projectId, @PathVariable Long diagramId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return toAjax(frontProjectAssetService.deleteDiagram(userId, projectId, diagramId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/paper")
|
||||
public AjaxResult paper(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(frontProjectAssetService.getPaper(userId, projectId));
|
||||
}
|
||||
|
||||
@PutMapping("/{projectId}/paper")
|
||||
public AjaxResult savePaper(@PathVariable Long projectId, @RequestBody FrontProjectPaper request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(frontProjectAssetService.savePaper(userId, projectId, request));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/page-designs/init")
|
||||
public AjaxResult initPageDesigns(@PathVariable Long projectId,
|
||||
@RequestParam(defaultValue = "frontend") String scope)
|
||||
@@ -135,6 +265,17 @@ public class FrontProjectController extends BaseController
|
||||
return AjaxResult.success(frontendPageDesignService.initializePageDesigns(userId, projectId, scope));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/page-designs/apply-business-loop-bindings")
|
||||
public AjaxResult applyBusinessLoopBindings(@PathVariable Long projectId,
|
||||
@RequestParam(defaultValue = "frontend") String scope)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
FrontProject project = frontProjectService.getProject(userId, projectId);
|
||||
frontendPageDesignService.applyBusinessLoopBindings(userId, projectId,
|
||||
FlowConfigProjector.compile(project).getLoopPlan(), scope);
|
||||
return AjaxResult.success(frontendPageDesignService.listPageDesigns(userId, projectId, scope));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/page-designs")
|
||||
public AjaxResult createPageDesign(@PathVariable Long projectId,
|
||||
@RequestParam(defaultValue = "frontend") String scope,
|
||||
@@ -152,6 +293,23 @@ public class FrontProjectController extends BaseController
|
||||
return AjaxResult.success(frontendPageDesignService.getPageDesign(userId, projectId, designId, scope));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/page-designs/{designId}/dsl")
|
||||
public AjaxResult pageDsl(@PathVariable Long projectId, @PathVariable Long designId,
|
||||
@RequestParam(defaultValue = "frontend") String scope)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(frontendPageDesignService.getPageDsl(userId, projectId, designId, scope));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/page-designs/{designId}/compose")
|
||||
public AjaxResult composePageDesign(@PathVariable Long projectId, @PathVariable Long designId,
|
||||
@RequestParam(defaultValue = "frontend") String scope,
|
||||
@RequestBody(required = false) FrontendPageDesignRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return AjaxResult.success(frontendPageDesignService.composePageDesign(userId, projectId, designId, request, scope));
|
||||
}
|
||||
|
||||
@PutMapping("/{projectId}/page-designs/{designId}")
|
||||
public AjaxResult savePageDesign(@PathVariable Long projectId, @PathVariable Long designId,
|
||||
@RequestParam(defaultValue = "frontend") String scope,
|
||||
@@ -184,11 +342,132 @@ public class FrontProjectController extends BaseController
|
||||
return success(aiGenerateService.generateAppBlueprint(userId, projectId, request));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/generate-business-blueprint")
|
||||
public AjaxResult generateBusinessBlueprint(@PathVariable Long projectId, @RequestBody GenerateBusinessBlueprintRequest request)
|
||||
@PostMapping("/{projectId}/generate-flow-config")
|
||||
public AjaxResult generateFlowConfig(@PathVariable Long projectId, @RequestBody GenerateFlowConfigRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(aiGenerateService.generateBusinessBlueprint(userId, projectId, request));
|
||||
return success(aiGenerateService.generateFlowConfig(userId, projectId, request));
|
||||
}
|
||||
|
||||
@PutMapping("/{projectId}/flow-config")
|
||||
public AjaxResult saveFlowConfig(@PathVariable Long projectId, @RequestBody SimpleFlowConfig flowConfig)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return toAjax(frontProjectService.updateFlowConfig(userId, projectId, JSON.toJSONString(flowConfig)));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/diagram")
|
||||
public AjaxResult generateDiagram(@PathVariable Long projectId, @RequestBody GenerateDiagramRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(aiGenerateService.generateDiagram(userId, projectId, request));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/paper-outline")
|
||||
public AjaxResult generatePaperOutline(@PathVariable Long projectId, @RequestBody GeneratePaperOutlineRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(aiGenerateService.generatePaperOutline(userId, projectId, request));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/paper-draft")
|
||||
public AjaxResult generatePaperDraft(@PathVariable Long projectId, @RequestBody GeneratePaperDraftRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(aiGenerateService.generatePaperDraft(userId, projectId, request));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/{projectId}/paper-draft/stream", produces = "text/plain;charset=UTF-8")
|
||||
public StreamingResponseBody streamPaperDraft(@PathVariable Long projectId,
|
||||
@RequestBody GeneratePaperDraftRequest request)
|
||||
{
|
||||
final Long userId = SecurityUtils.getUserId();
|
||||
return outputStream ->
|
||||
{
|
||||
final Writer writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
|
||||
aiGenerateService.streamPaperDraft(userId, projectId, request, content ->
|
||||
{
|
||||
writer.write(content);
|
||||
writer.flush();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/code-analysis")
|
||||
public AjaxResult createCodeAnalysis(@PathVariable Long projectId,
|
||||
@RequestBody(required = false) GenerateCodeAnalysisRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
assertCodeAnalysisPreviewReady(userId, projectId);
|
||||
return success(aiGenerationTaskService.createTask(userId, projectId, toCodeAnalysisTaskRequest(request)));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/{projectId}/code-analysis/stream", produces = "text/plain;charset=UTF-8")
|
||||
public StreamingResponseBody streamCodeAnalysis(@PathVariable Long projectId,
|
||||
@RequestBody(required = false) GenerateCodeAnalysisRequest request)
|
||||
{
|
||||
final Long userId = SecurityUtils.getUserId();
|
||||
assertCodeAnalysisPreviewReady(userId, projectId);
|
||||
return outputStream ->
|
||||
{
|
||||
final Writer writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
|
||||
aiGenerateService.streamCodeAnalysis(userId, projectId, request, content ->
|
||||
{
|
||||
writer.write(content);
|
||||
writer.flush();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/code-analysis/history")
|
||||
public AjaxResult codeAnalysisHistory(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(aiGenerationTaskService.listGenerations(userId, projectId, "code_analysis"));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/code-analysis/{generationId}/continue")
|
||||
public AjaxResult continueCodeAnalysis(@PathVariable Long projectId, @PathVariable Long generationId,
|
||||
@RequestBody(required = false) GenerateCodeAnalysisRequest request)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
assertCodeAnalysisPreviewReady(userId, projectId);
|
||||
FrontProjectGeneration previous = aiGenerationTaskService.getGeneration(userId, projectId, generationId);
|
||||
if (!"code_analysis".equals(previous.getGenerateType()))
|
||||
{
|
||||
throw new ServiceException("只能基于代码解读历史继续生成");
|
||||
}
|
||||
GenerateCodeAnalysisRequest nextRequest = request == null ? new GenerateCodeAnalysisRequest() : request;
|
||||
nextRequest.setMode("continue");
|
||||
nextRequest.setPreviousGenerationId(generationId);
|
||||
nextRequest.setPreviousMarkdown(readCodeAnalysisMarkdown(previous.getResponsePayload()));
|
||||
return success(aiGenerationTaskService.createTask(userId, projectId, toCodeAnalysisTaskRequest(nextRequest)));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/{projectId}/code-analysis/{generationId}/continue/stream", produces = "text/plain;charset=UTF-8")
|
||||
public StreamingResponseBody continueCodeAnalysisStream(@PathVariable Long projectId, @PathVariable Long generationId,
|
||||
@RequestBody(required = false) GenerateCodeAnalysisRequest request)
|
||||
{
|
||||
final Long userId = SecurityUtils.getUserId();
|
||||
assertCodeAnalysisPreviewReady(userId, projectId);
|
||||
final GenerateCodeAnalysisRequest nextRequest = toContinueCodeAnalysisRequest(userId, projectId, generationId,
|
||||
request);
|
||||
return outputStream ->
|
||||
{
|
||||
final Writer writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
|
||||
aiGenerateService.streamCodeAnalysis(userId, projectId, nextRequest, content ->
|
||||
{
|
||||
writer.write(content);
|
||||
writer.flush();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@GetMapping("/ai-tasks/one-click-overview")
|
||||
public AjaxResult oneClickTaskOverview()
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(aiGenerationTaskService.listOneClickTaskOverview(userId));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/ai-tasks")
|
||||
@@ -198,6 +477,62 @@ public class FrontProjectController extends BaseController
|
||||
return success(aiGenerationTaskService.createTask(userId, projectId, request));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/ai-tasks/one-click-history")
|
||||
public AjaxResult oneClickTaskHistory(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(aiGenerationTaskService.listOneClickTaskHistory(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/delivery-readiness")
|
||||
public AjaxResult deliveryReadiness(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(projectDeliveryReadinessService.get(userId, projectId));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/quality-runs")
|
||||
public AjaxResult createQualityRun(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(projectQualityRunService.createSummary(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/quality-runs")
|
||||
public AjaxResult qualityRuns(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(projectQualityRunService.listSummaries(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/quality-runs/latest")
|
||||
public AjaxResult latestQualityRun(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(projectQualityRunService.latestSummary(userId, projectId));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/v1-acceptance-runs")
|
||||
public AjaxResult startV1Acceptance(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(factoryV1AcceptanceService.start(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/v1-acceptance-runs/latest")
|
||||
public AjaxResult latestV1Acceptance(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(factoryV1AcceptanceService.latest(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/v1-acceptance-runs")
|
||||
public AjaxResult v1Acceptances(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(factoryV1AcceptanceService.list(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/ai-tasks/{taskId}")
|
||||
public AjaxResult getAiTask(@PathVariable Long projectId, @PathVariable Long taskId)
|
||||
{
|
||||
@@ -226,6 +561,35 @@ public class FrontProjectController extends BaseController
|
||||
return success(aiGenerationTaskService.listGenerations(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/business-loop-audit")
|
||||
public AjaxResult businessLoopAudit(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
FrontProject project = frontProjectService.getProject(userId, projectId);
|
||||
SimpleFlowConfig flowConfig = FlowConfigProjector.read(project);
|
||||
FlowConfigCompilation flow = flowConfig == null
|
||||
? new FlowConfigCompilation() : flowConfigContractService.compile(flowConfig);
|
||||
BusinessLoopPlan plan = flow.getLoopPlan();
|
||||
DatabaseDesignResponse database = frontProjectService.getDatabase(userId, projectId);
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>();
|
||||
BusinessLoopAuditResult databaseAudit = flowConfig == null
|
||||
? businessLoopCoverageValidator.auditDatabase(plan, database)
|
||||
: flowConfigContractService.auditDatabase(flowConfig, database);
|
||||
BusinessLoopAuditResult actionAudit = businessLoopCoverageValidator.auditBusinessActions(plan,
|
||||
flow.getBusinessActions());
|
||||
String requirement = StringUtils.defaultString(project.getProjectDesc());
|
||||
if (StringUtils.isNotBlank(project.getRequirementKeyword()))
|
||||
{
|
||||
requirement += "\n补充要求:" + project.getRequirementKeyword();
|
||||
}
|
||||
BusinessLoopAuditResult requirementAudit = businessRequirementCoverageValidator.audit(requirement, flowConfig);
|
||||
result.put("requirements", requirementAudit);
|
||||
result.put("database", databaseAudit);
|
||||
result.put("businessActions", actionAudit);
|
||||
result.put("complete", requirementAudit.isComplete() && databaseAudit.isComplete() && actionAudit.isComplete());
|
||||
return success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/ai-quota")
|
||||
public AjaxResult aiQuota()
|
||||
{
|
||||
@@ -237,14 +601,10 @@ public class FrontProjectController extends BaseController
|
||||
public AjaxResult preview(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
FrontProject project = frontProjectService.getProject(userId, projectId);
|
||||
frontProjectPreviewService.getStructure(userId, projectId, "backend");
|
||||
if (!"0".equals(project.getFrontendEnabled()))
|
||||
for (String type : frontProjectPreviewService.getSupportedTemplateTypes(userId, projectId))
|
||||
{
|
||||
frontProjectPreviewService.getStructure(userId, projectId, "frontend");
|
||||
frontProjectPreviewService.getStructure(userId, projectId, type);
|
||||
}
|
||||
frontProjectPreviewService.getStructure(userId, projectId, "admin_frontend");
|
||||
frontProjectPreviewService.getStructure(userId, projectId, "sql");
|
||||
return toAjax(frontProjectPreviewService.markPreviewReady(userId, projectId));
|
||||
}
|
||||
|
||||
@@ -269,6 +629,18 @@ public class FrontProjectController extends BaseController
|
||||
return success(frontProjectRunPreviewService.stop(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/run-preview/{previewJobId}/screenshot")
|
||||
public void runPreviewScreenshot(@PathVariable Long projectId, @PathVariable Long previewJobId,
|
||||
HttpServletResponse response) throws IOException
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
byte[] data = frontProjectRunPreviewService.screenshot(userId, projectId, previewJobId);
|
||||
response.reset();
|
||||
response.setContentType("image/png");
|
||||
response.addHeader("Content-Length", String.valueOf(data.length));
|
||||
IOUtils.write(data, response.getOutputStream());
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/structure")
|
||||
public AjaxResult structure(@PathVariable Long projectId, @RequestParam String templateType)
|
||||
{
|
||||
@@ -276,6 +648,13 @@ public class FrontProjectController extends BaseController
|
||||
return success(frontProjectPreviewService.getStructure(userId, projectId, templateType));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/template-types")
|
||||
public AjaxResult templateTypes(@PathVariable Long projectId)
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
return success(frontProjectPreviewService.getSupportedTemplateTypes(userId, projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/file-content")
|
||||
public AjaxResult fileContent(@PathVariable Long projectId, PreviewFileRequest request)
|
||||
{
|
||||
@@ -284,15 +663,50 @@ public class FrontProjectController extends BaseController
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/download")
|
||||
public void download(@PathVariable Long projectId, @RequestParam(required = false) String templateType, HttpServletResponse response) throws IOException
|
||||
public void download(@PathVariable Long projectId,
|
||||
@RequestParam(required = false) String templateType,
|
||||
@RequestParam(required = false, defaultValue = ProjectDeliveryReadiness.ARTIFACT_DRAFT) String artifactType,
|
||||
HttpServletResponse response) throws IOException
|
||||
{
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
String normalizedArtifactType = StringUtils.isEmpty(artifactType)
|
||||
? ProjectDeliveryReadiness.ARTIFACT_DRAFT
|
||||
: artifactType.trim().toUpperCase(Locale.ROOT);
|
||||
ProjectDeliveryReadiness readiness = projectDeliveryReadinessService
|
||||
.assertDownloadAllowed(userId, projectId, normalizedArtifactType);
|
||||
FrontProject project = frontProjectService.getProject(userId, projectId);
|
||||
byte[] data = StringUtils.isNotEmpty(templateType)
|
||||
? frontProjectPreviewService.download(userId, projectId, templateType)
|
||||
: frontProjectPreviewService.downloadAll(userId, projectId);
|
||||
ProjectCertifiedArtifact certifiedArtifact = null;
|
||||
byte[] data;
|
||||
if (ProjectDeliveryReadiness.ARTIFACT_CERTIFIED.equals(normalizedArtifactType))
|
||||
{
|
||||
if (StringUtils.isNotEmpty(templateType))
|
||||
{
|
||||
throw new ServiceException("认证制品只支持下载完整冻结源码");
|
||||
}
|
||||
if (readiness.getQualityRun() == null || readiness.getQualityRun().getQualityRunId() == null)
|
||||
{
|
||||
throw new ServiceException("认证制品缺少质量运行身份");
|
||||
}
|
||||
certifiedArtifact = certifiedArtifactService.getReady(userId, projectId,
|
||||
readiness.getQualityRun().getQualityRunId());
|
||||
data = certifiedArtifactService.read(certifiedArtifact);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = StringUtils.isNotEmpty(templateType)
|
||||
? frontProjectPreviewService.download(userId, projectId, templateType)
|
||||
: frontProjectPreviewService.downloadAll(userId, projectId);
|
||||
}
|
||||
response.reset();
|
||||
FileUtils.setAttachmentResponseHeader(response, sourceZipName(project));
|
||||
response.addHeader("X-Factory-Artifact-Type", normalizedArtifactType);
|
||||
response.addHeader("X-Factory-Certification-Level", readiness.getCertificationLevel());
|
||||
if (certifiedArtifact != null)
|
||||
{
|
||||
response.addHeader("X-Factory-Artifact-Id", String.valueOf(certifiedArtifact.getArtifactId()));
|
||||
response.addHeader("X-Factory-Zip-SHA256", certifiedArtifact.getZipSha256());
|
||||
response.addHeader("X-Factory-Report-SHA256", certifiedArtifact.getReportSha256());
|
||||
}
|
||||
response.addHeader("Content-Length", "" + data.length);
|
||||
response.setContentType("application/octet-stream; charset=UTF-8");
|
||||
IOUtils.write(data, response.getOutputStream());
|
||||
@@ -311,4 +725,76 @@ public class FrontProjectController extends BaseController
|
||||
}
|
||||
return safeName + ".zip";
|
||||
}
|
||||
|
||||
private AiGenerationTaskCreateRequest toCodeAnalysisTaskRequest(GenerateCodeAnalysisRequest source)
|
||||
{
|
||||
AiGenerationTaskCreateRequest request = new AiGenerationTaskCreateRequest();
|
||||
request.setGenerateType("code_analysis");
|
||||
if (source != null)
|
||||
{
|
||||
request.setMode(source.getMode());
|
||||
request.setPreviousGenerationId(source.getPreviousGenerationId());
|
||||
request.setPreviousMarkdown(source.getPreviousMarkdown());
|
||||
request.setExtraRequirements(source.getExtraRequirements());
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private void assertCodeAnalysisPreviewReady(Long userId, Long projectId)
|
||||
{
|
||||
FrontProject project = frontProjectService.getProject(userId, projectId);
|
||||
if (project == null || !"1".equals(project.getPreviewStatus()))
|
||||
{
|
||||
throw new ServiceException("请先生成源码预览后再进行代码解读");
|
||||
}
|
||||
}
|
||||
|
||||
private GenerateCodeAnalysisRequest toContinueCodeAnalysisRequest(Long userId, Long projectId, Long generationId,
|
||||
GenerateCodeAnalysisRequest request)
|
||||
{
|
||||
FrontProjectGeneration previous = aiGenerationTaskService.getGeneration(userId, projectId, generationId);
|
||||
if (!"code_analysis".equals(previous.getGenerateType()))
|
||||
{
|
||||
throw new ServiceException("只能基于代码解读历史继续生成");
|
||||
}
|
||||
GenerateCodeAnalysisRequest nextRequest = request == null ? new GenerateCodeAnalysisRequest() : request;
|
||||
nextRequest.setMode("continue");
|
||||
nextRequest.setPreviousGenerationId(generationId);
|
||||
if (StringUtils.isBlank(nextRequest.getPreviousMarkdown()))
|
||||
{
|
||||
nextRequest.setPreviousMarkdown(readCodeAnalysisMarkdown(previous.getResponsePayload()));
|
||||
}
|
||||
return nextRequest;
|
||||
}
|
||||
|
||||
private String readCodeAnalysisMarkdown(String responsePayload)
|
||||
{
|
||||
if (StringUtils.isEmpty(responsePayload))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
try
|
||||
{
|
||||
CodeAnalysisResponse response = JSON.parseObject(responsePayload, CodeAnalysisResponse.class);
|
||||
if (response != null && StringUtils.isNotEmpty(response.getMarkdown()))
|
||||
{
|
||||
return response.getMarkdown();
|
||||
}
|
||||
}
|
||||
catch (RuntimeException ignored)
|
||||
{
|
||||
return responsePayload;
|
||||
}
|
||||
return responsePayload;
|
||||
}
|
||||
|
||||
private BusinessLoopPlan readBusinessLoopPlan(String json)
|
||||
{
|
||||
if (StringUtils.isEmpty(json))
|
||||
{
|
||||
return new BusinessLoopPlan();
|
||||
}
|
||||
BusinessLoopPlan plan = JSON.parseObject(json, BusinessLoopPlan.class);
|
||||
return plan == null ? new BusinessLoopPlan() : plan;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.web.service.TokenService;
|
||||
import com.ruoyi.generator.domain.front.FrontUser;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontPasswordUpdateBody;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontProfileUpdateBody;
|
||||
import com.ruoyi.generator.service.front.IFrontUserService;
|
||||
|
||||
@Component
|
||||
@@ -68,6 +70,79 @@ public class FrontAuthService
|
||||
return frontUser;
|
||||
}
|
||||
|
||||
public FrontUser updateProfile(Long userId, FrontProfileUpdateBody body)
|
||||
{
|
||||
if (userId == null)
|
||||
{
|
||||
throw new ServiceException("登录状态异常,请重新登录");
|
||||
}
|
||||
if (body == null)
|
||||
{
|
||||
throw new ServiceException("用户资料不能为空");
|
||||
}
|
||||
|
||||
String nickname = trimToEmpty(body.getNickname());
|
||||
String email = trimToEmpty(body.getEmail());
|
||||
String phone = trimToEmpty(body.getPhone());
|
||||
validateProfile(nickname, email, phone);
|
||||
|
||||
FrontUser user = new FrontUser();
|
||||
user.setUserId(userId);
|
||||
user.setNickname(nickname);
|
||||
user.setEmail(email);
|
||||
user.setPhone(phone);
|
||||
|
||||
if (frontUserService.updateProfile(user) <= 0)
|
||||
{
|
||||
throw new ServiceException("更新个人资料失败");
|
||||
}
|
||||
return profile(userId);
|
||||
}
|
||||
|
||||
public int updatePassword(Long userId, FrontPasswordUpdateBody body)
|
||||
{
|
||||
if (userId == null)
|
||||
{
|
||||
throw new ServiceException("登录状态异常,请重新登录");
|
||||
}
|
||||
if (body == null || StringUtils.isEmpty(body.getOldPassword()))
|
||||
{
|
||||
throw new ServiceException("旧密码不能为空");
|
||||
}
|
||||
if (StringUtils.isEmpty(body.getNewPassword()))
|
||||
{
|
||||
throw new ServiceException("新密码不能为空");
|
||||
}
|
||||
if (!body.getNewPassword().equals(body.getConfirmPassword()))
|
||||
{
|
||||
throw new ServiceException("两次输入的新密码不一致");
|
||||
}
|
||||
if (body.getNewPassword().length() < 5 || body.getNewPassword().length() > 50)
|
||||
{
|
||||
throw new ServiceException("新密码长度必须在5到50个字符之间");
|
||||
}
|
||||
|
||||
FrontUser existing = frontUserService.selectById(userId);
|
||||
if (existing == null || StringUtils.isEmpty(existing.getPassword()))
|
||||
{
|
||||
throw new ServiceException("用户不存在");
|
||||
}
|
||||
if (!matchesPassword(body.getOldPassword(), existing.getPassword()))
|
||||
{
|
||||
throw new ServiceException("旧密码错误");
|
||||
}
|
||||
|
||||
FrontUser user = new FrontUser();
|
||||
user.setUserId(userId);
|
||||
user.setPassword(SecurityUtils.encryptPassword(body.getNewPassword()));
|
||||
int rows = frontUserService.updatePassword(user);
|
||||
if (rows <= 0)
|
||||
{
|
||||
throw new ServiceException("修改密码失败");
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
private boolean matchesPassword(String password, String encodedPassword)
|
||||
{
|
||||
try
|
||||
@@ -80,6 +155,35 @@ public class FrontAuthService
|
||||
}
|
||||
}
|
||||
|
||||
private void validateProfile(String nickname, String email, String phone)
|
||||
{
|
||||
if (nickname.length() > 50)
|
||||
{
|
||||
throw new ServiceException("昵称长度不能超过50个字符");
|
||||
}
|
||||
if (email.length() > 100)
|
||||
{
|
||||
throw new ServiceException("邮箱长度不能超过100个字符");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(email) && !email.matches("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"))
|
||||
{
|
||||
throw new ServiceException("邮箱格式不正确");
|
||||
}
|
||||
if (phone.length() > 20)
|
||||
{
|
||||
throw new ServiceException("手机号长度不能超过20个字符");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(phone) && !phone.matches("^[0-9+\\- ]+$"))
|
||||
{
|
||||
throw new ServiceException("手机号格式不正确");
|
||||
}
|
||||
}
|
||||
|
||||
private String trimToEmpty(String value)
|
||||
{
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
private ServiceException credentialException()
|
||||
{
|
||||
return new ServiceException("用户名或密码错误");
|
||||
|
||||
@@ -17,12 +17,61 @@ ruoyi:
|
||||
deepseek:
|
||||
apiKey: sk-2aef3231d45048a9a0ba6f0367a072ca
|
||||
baseUrl: https://api.deepseek.com
|
||||
model: deepseek-v4-flash
|
||||
model: ${DEEPSEEK_MODEL:deepseek-v4-flash}
|
||||
timeout: ${DEEPSEEK_TIMEOUT:180000}
|
||||
maxTables: 12
|
||||
maxColumnsPerTable: 30
|
||||
skipSslVerify: true
|
||||
|
||||
easycode:
|
||||
one-click:
|
||||
run-preview-timeout-ms: ${EASYCODE_ONE_CLICK_RUN_PREVIEW_TIMEOUT_MS:300000}
|
||||
run-preview-poll-interval-ms: ${EASYCODE_ONE_CLICK_RUN_PREVIEW_POLL_INTERVAL_MS:1000}
|
||||
preview:
|
||||
execution-mode: ${EASYCODE_PREVIEW_EXECUTION_MODE:host}
|
||||
ttl-minutes: ${EASYCODE_PREVIEW_TTL_MINUTES:15}
|
||||
worker-enabled: ${EASYCODE_PREVIEW_WORKER_ENABLED:false}
|
||||
worker-root: ${EASYCODE_PREVIEW_WORKER_ROOT:preview-worker-data}
|
||||
docker-image: ${EASYCODE_PREVIEW_DOCKER_IMAGE:easycode/preview-worker:v1}
|
||||
docker-network: ${EASYCODE_PREVIEW_DOCKER_NETWORK:bridge}
|
||||
bind-host: ${EASYCODE_PREVIEW_BIND_HOST:127.0.0.1}
|
||||
cpu-limit: ${EASYCODE_PREVIEW_CPU_LIMIT:1.0}
|
||||
memory-limit-mb: ${EASYCODE_PREVIEW_MEMORY_LIMIT_MB:1024}
|
||||
pids-limit: ${EASYCODE_PREVIEW_PIDS_LIMIT:256}
|
||||
|
||||
factory:
|
||||
prompt-registry:
|
||||
database-enabled: ${FACTORY_PROMPT_REGISTRY_DATABASE_ENABLED:true}
|
||||
builtin-fallback-enabled: ${FACTORY_PROMPT_REGISTRY_BUILTIN_FALLBACK_ENABLED:false}
|
||||
plugin-registry:
|
||||
database-enabled: ${FACTORY_PLUGIN_REGISTRY_DATABASE_ENABLED:false}
|
||||
plugin-execution:
|
||||
executor-code: ${FACTORY_PLUGIN_EXECUTOR_CODE:dry-run:v1}
|
||||
environment-code: ${FACTORY_PLUGIN_EXECUTION_ENVIRONMENT_CODE:development}
|
||||
approval-required-for-non-dry-run: ${FACTORY_PLUGIN_APPROVAL_REQUIRED_FOR_NON_DRY_RUN:true}
|
||||
approval-separation-enabled: ${FACTORY_PLUGIN_APPROVAL_SEPARATION_ENABLED:true}
|
||||
approval-request-ttl-minutes: ${FACTORY_PLUGIN_APPROVAL_REQUEST_TTL_MINUTES:1440}
|
||||
approval-validity-minutes: ${FACTORY_PLUGIN_APPROVAL_VALIDITY_MINUTES:30}
|
||||
approval-requester-role-keys: ${FACTORY_PLUGIN_APPROVAL_REQUESTER_ROLE_KEYS:}
|
||||
approval-approver-role-keys: ${FACTORY_PLUGIN_APPROVAL_APPROVER_ROLE_KEYS:}
|
||||
executor-routes:
|
||||
database-migration-sql: ${FACTORY_PLUGIN_DATABASE_MIGRATION_SQL_EXECUTOR_CODE:}
|
||||
permission-backend: ${FACTORY_PLUGIN_PERMISSION_BACKEND_EXECUTOR_CODE:}
|
||||
menu-admin-frontend: ${FACTORY_PLUGIN_MENU_ADMIN_FRONTEND_EXECUTOR_CODE:}
|
||||
outbox-polling-enabled: ${FACTORY_PLUGIN_OUTBOX_POLLING_ENABLED:true}
|
||||
outbox-poll-interval-seconds: ${FACTORY_PLUGIN_OUTBOX_POLL_INTERVAL_SECONDS:5}
|
||||
outbox-batch-size: ${FACTORY_PLUGIN_OUTBOX_BATCH_SIZE:5}
|
||||
outbox-lease-seconds: ${FACTORY_PLUGIN_OUTBOX_LEASE_SECONDS:60}
|
||||
sql-target:
|
||||
enabled: ${FACTORY_PLUGIN_SQL_TARGET_ENABLED:false}
|
||||
environment-code: ${FACTORY_PLUGIN_SQL_TARGET_ENVIRONMENT_CODE:}
|
||||
jdbc-url: ${FACTORY_PLUGIN_SQL_TARGET_JDBC_URL:}
|
||||
username: ${FACTORY_PLUGIN_SQL_TARGET_USERNAME:}
|
||||
password: ${FACTORY_PLUGIN_SQL_TARGET_PASSWORD:}
|
||||
driver-class-name: ${FACTORY_PLUGIN_SQL_TARGET_DRIVER_CLASS_NAME:com.mysql.cj.jdbc.Driver}
|
||||
allowed-catalog: ${FACTORY_PLUGIN_SQL_TARGET_ALLOWED_CATALOG:}
|
||||
receipt-lease-seconds: ${FACTORY_PLUGIN_SQL_TARGET_RECEIPT_LEASE_SECONDS:300}
|
||||
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
@@ -64,13 +113,16 @@ spring:
|
||||
basename: i18n/messages
|
||||
profiles:
|
||||
active: druid
|
||||
mvc:
|
||||
async:
|
||||
request-timeout: ${SPRING_MVC_ASYNC_REQUEST_TIMEOUT:600000}
|
||||
# 文件上传
|
||||
servlet:
|
||||
multipart:
|
||||
# 单个文件大小
|
||||
max-file-size: 10MB
|
||||
max-file-size: ${SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE:100MB}
|
||||
# 设置总上传的文件大小
|
||||
max-request-size: 20MB
|
||||
max-request-size: ${SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE:120MB}
|
||||
# 服务模块
|
||||
devtools:
|
||||
restart:
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.ruoyi.web.config;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
public class ApplicationYamlTimeoutTest
|
||||
{
|
||||
@Test
|
||||
public void mvcAsyncRequestTimeoutCoversDeepSeekTimeout()
|
||||
{
|
||||
Properties properties = loadApplicationProperties();
|
||||
String asyncTimeout = properties.getProperty("spring.mvc.async.request-timeout");
|
||||
String deepSeekTimeout = properties.getProperty("deepseek.timeout");
|
||||
|
||||
assertNotNull("spring.mvc.async.request-timeout must be configured for streaming responses", asyncTimeout);
|
||||
assertNotNull("deepseek.timeout must be configured", deepSeekTimeout);
|
||||
assertTrue("spring.mvc.async.request-timeout should be at least deepseek.timeout",
|
||||
toMillis(asyncTimeout) >= toMillis(deepSeekTimeout));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartUploadLimitSupportsTemplateImportZip()
|
||||
{
|
||||
Properties properties = loadApplicationProperties();
|
||||
String maxFileSize = properties.getProperty("spring.servlet.multipart.max-file-size");
|
||||
String maxRequestSize = properties.getProperty("spring.servlet.multipart.max-request-size");
|
||||
|
||||
assertNotNull("spring.servlet.multipart.max-file-size must be configured", maxFileSize);
|
||||
assertNotNull("spring.servlet.multipart.max-request-size must be configured", maxRequestSize);
|
||||
assertTrue("template import needs to accept project ZIP files up to 100MB",
|
||||
toBytes(maxFileSize) >= 100L * 1024L * 1024L);
|
||||
assertTrue("template import request limit must cover a 100MB ZIP plus form fields",
|
||||
toBytes(maxRequestSize) >= 100L * 1024L * 1024L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oneClickRunPreviewTimeoutAllowsSlowLocalStartup()
|
||||
{
|
||||
Properties properties = loadApplicationProperties();
|
||||
String runPreviewTimeout = properties.getProperty("easycode.one-click.run-preview-timeout-ms");
|
||||
String pollInterval = properties.getProperty("easycode.one-click.run-preview-poll-interval-ms");
|
||||
|
||||
assertNotNull("one-click run preview timeout must be configurable", runPreviewTimeout);
|
||||
assertNotNull("one-click run preview polling interval must be configurable", pollInterval);
|
||||
assertTrue("one-click run preview timeout should allow slow Maven and frontend startup",
|
||||
toMillis(runPreviewTimeout) >= 300000L);
|
||||
assertTrue("one-click run preview polling interval must be positive",
|
||||
toMillis(pollInterval) > 0L);
|
||||
}
|
||||
|
||||
private Properties loadApplicationProperties()
|
||||
{
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(new ClassPathResource("application.yml"));
|
||||
return factory.getObject();
|
||||
}
|
||||
|
||||
private long toMillis(String rawValue)
|
||||
{
|
||||
String value = defaultValue(rawValue).trim().toLowerCase(Locale.ROOT);
|
||||
if (value.endsWith("ms"))
|
||||
{
|
||||
return Long.parseLong(value.substring(0, value.length() - 2));
|
||||
}
|
||||
if (value.endsWith("s"))
|
||||
{
|
||||
return Long.parseLong(value.substring(0, value.length() - 1)) * 1000L;
|
||||
}
|
||||
if (value.endsWith("m"))
|
||||
{
|
||||
return Long.parseLong(value.substring(0, value.length() - 1)) * 60L * 1000L;
|
||||
}
|
||||
if (value.endsWith("h"))
|
||||
{
|
||||
return Long.parseLong(value.substring(0, value.length() - 1)) * 60L * 60L * 1000L;
|
||||
}
|
||||
return Long.parseLong(value);
|
||||
}
|
||||
|
||||
private long toBytes(String rawValue)
|
||||
{
|
||||
String value = defaultValue(rawValue).trim().toLowerCase(Locale.ROOT);
|
||||
if (value.endsWith("kb"))
|
||||
{
|
||||
return Long.parseLong(value.substring(0, value.length() - 2)) * 1024L;
|
||||
}
|
||||
if (value.endsWith("mb"))
|
||||
{
|
||||
return Long.parseLong(value.substring(0, value.length() - 2)) * 1024L * 1024L;
|
||||
}
|
||||
if (value.endsWith("gb"))
|
||||
{
|
||||
return Long.parseLong(value.substring(0, value.length() - 2)) * 1024L * 1024L * 1024L;
|
||||
}
|
||||
return Long.parseLong(value);
|
||||
}
|
||||
|
||||
private String defaultValue(String rawValue)
|
||||
{
|
||||
String value = rawValue == null ? "" : rawValue.trim();
|
||||
if (value.startsWith("${") && value.endsWith("}"))
|
||||
{
|
||||
int colon = value.indexOf(':');
|
||||
if (colon >= 0)
|
||||
{
|
||||
return value.substring(colon + 1, value.length() - 1);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
package com.ruoyi.web.controller.front;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.generator.factory.dsl.ProjectSpecQueryService;
|
||||
import com.ruoyi.generator.factory.dsl.model.ProjectSpecDocument;
|
||||
import com.ruoyi.generator.factory.dsl.model.ProjectSpecV1;
|
||||
import com.ruoyi.generator.factory.diff.ProjectSpecVersionDiffService;
|
||||
import com.ruoyi.generator.factory.diff.model.ProjectSpecDiffResult;
|
||||
import com.ruoyi.generator.factory.generation.ProjectSpecGenerationService;
|
||||
import com.ruoyi.generator.factory.generation.ProjectSpecGenerationRunService;
|
||||
import com.ruoyi.generator.factory.generation.model.ProjectSpecGenerationResult;
|
||||
import com.ruoyi.generator.factory.generation.model.ProjectSpecGenerationRunDiffResult;
|
||||
import com.ruoyi.generator.factory.patch.ProjectSpecPatchService;
|
||||
import com.ruoyi.generator.factory.patch.model.ProjectSpecPatchRequest;
|
||||
import com.ruoyi.generator.factory.patch.model.ProjectSpecPatchResult;
|
||||
import com.ruoyi.generator.factory.validation.ProjectSpecValidationService;
|
||||
import com.ruoyi.generator.factory.validation.model.ProjectSpecValidationResult;
|
||||
import com.ruoyi.generator.factory.version.ProjectSpecVersionService;
|
||||
import com.ruoyi.generator.factory.version.model.ProjectSpecRollbackRequest;
|
||||
import com.ruoyi.generator.factory.version.model.ProjectSpecSnapshotRequest;
|
||||
import com.ruoyi.generator.factory.version.model.ProjectSpecVersionView;
|
||||
|
||||
public class FrontFactoryProjectControllerTest
|
||||
{
|
||||
@Mock
|
||||
private ProjectSpecQueryService projectSpecQueryService;
|
||||
@Mock
|
||||
private ProjectSpecVersionService projectSpecVersionService;
|
||||
@Mock
|
||||
private ProjectSpecValidationService projectSpecValidationService;
|
||||
@Mock
|
||||
private ProjectSpecPatchService projectSpecPatchService;
|
||||
@Mock
|
||||
private ProjectSpecVersionDiffService projectSpecVersionDiffService;
|
||||
@Mock
|
||||
private ProjectSpecGenerationService projectSpecGenerationService;
|
||||
@Mock
|
||||
private ProjectSpecGenerationRunService projectSpecGenerationRunService;
|
||||
|
||||
private FrontFactoryProjectController controller;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
MockitoAnnotations.initMocks(this);
|
||||
controller = new FrontFactoryProjectController();
|
||||
Field field = FrontFactoryProjectController.class.getDeclaredField("projectSpecQueryService");
|
||||
field.setAccessible(true);
|
||||
field.set(controller, projectSpecQueryService);
|
||||
field = FrontFactoryProjectController.class.getDeclaredField("projectSpecVersionService");
|
||||
field.setAccessible(true);
|
||||
field.set(controller, projectSpecVersionService);
|
||||
field = FrontFactoryProjectController.class.getDeclaredField("projectSpecValidationService");
|
||||
field.setAccessible(true);
|
||||
field.set(controller, projectSpecValidationService);
|
||||
field = FrontFactoryProjectController.class.getDeclaredField("projectSpecPatchService");
|
||||
field.setAccessible(true);
|
||||
field.set(controller, projectSpecPatchService);
|
||||
field = FrontFactoryProjectController.class.getDeclaredField("projectSpecVersionDiffService");
|
||||
field.setAccessible(true);
|
||||
field.set(controller, projectSpecVersionDiffService);
|
||||
field = FrontFactoryProjectController.class.getDeclaredField("projectSpecGenerationService");
|
||||
field.setAccessible(true);
|
||||
field.set(controller, projectSpecGenerationService);
|
||||
field = FrontFactoryProjectController.class.getDeclaredField("projectSpecGenerationRunService");
|
||||
field.setAccessible(true);
|
||||
field.set(controller, projectSpecGenerationRunService);
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken(loginUser(), null));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown()
|
||||
{
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readsCurrentUsersProjectSpec()
|
||||
{
|
||||
ProjectSpecDocument document = new ProjectSpecDocument();
|
||||
document.setContentHash("hash");
|
||||
when(projectSpecQueryService.getCurrentSpec(7L, 20L)).thenReturn(document);
|
||||
|
||||
AjaxResult result = controller.getCurrentSpec(20L);
|
||||
|
||||
assertEquals(document, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecQueryService).getCurrentSpec(7L, 20L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validatesSubmittedProjectSpecForCurrentUser()
|
||||
{
|
||||
ProjectSpecV1 spec = new ProjectSpecV1();
|
||||
ProjectSpecValidationResult validation = new ProjectSpecValidationResult();
|
||||
when(projectSpecValidationService.validate(7L, 20L, spec)).thenReturn(validation);
|
||||
|
||||
AjaxResult result = controller.validateSpec(20L, spec);
|
||||
|
||||
assertEquals(validation, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecValidationService).validate(7L, 20L, spec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void previewsProjectSpecPatchForCurrentUser()
|
||||
{
|
||||
ProjectSpecPatchRequest request = new ProjectSpecPatchRequest();
|
||||
ProjectSpecPatchResult preview = new ProjectSpecPatchResult();
|
||||
when(projectSpecPatchService.preview(7L, 20L, request)).thenReturn(preview);
|
||||
|
||||
AjaxResult result = controller.previewSpecPatch(20L, request);
|
||||
|
||||
assertEquals(preview, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecPatchService).preview(7L, 20L, request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appliesProjectSpecPatchForCurrentUser()
|
||||
{
|
||||
ProjectSpecPatchRequest request = new ProjectSpecPatchRequest();
|
||||
ProjectSpecPatchResult applied = new ProjectSpecPatchResult();
|
||||
applied.setApplied(true);
|
||||
when(projectSpecPatchService.apply(7L, 20L, request)).thenReturn(applied);
|
||||
|
||||
AjaxResult result = controller.applySpecPatch(20L, request);
|
||||
|
||||
assertEquals(applied, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecPatchService).apply(7L, 20L, request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void snapshotsCurrentUsersProjectSpec()
|
||||
{
|
||||
ProjectSpecSnapshotRequest request = new ProjectSpecSnapshotRequest();
|
||||
request.setChangeSummary("Before generator migration");
|
||||
ProjectSpecVersionView version = version(101L, 1);
|
||||
when(projectSpecVersionService.snapshotCurrent(7L, 20L, "Before generator migration"))
|
||||
.thenReturn(version);
|
||||
|
||||
AjaxResult result = controller.snapshotCurrentSpec(20L, request);
|
||||
|
||||
assertEquals(version, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecVersionService).snapshotCurrent(7L, 20L, "Before generator migration");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listsCurrentUsersProjectSpecVersions()
|
||||
{
|
||||
ProjectSpecVersionView first = version(102L, 2);
|
||||
ProjectSpecVersionView second = version(101L, 1);
|
||||
when(projectSpecVersionService.listVersions(7L, 20L)).thenReturn(Arrays.asList(first, second));
|
||||
|
||||
AjaxResult result = controller.listSpecVersions(20L);
|
||||
|
||||
assertEquals(Arrays.asList(first, second), result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecVersionService).listVersions(7L, 20L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readsCurrentUsersProjectSpecVersion()
|
||||
{
|
||||
ProjectSpecVersionView version = version(101L, 1);
|
||||
when(projectSpecVersionService.getVersion(7L, 20L, 101L)).thenReturn(version);
|
||||
|
||||
AjaxResult result = controller.getSpecVersion(20L, 101L);
|
||||
|
||||
assertEquals(version, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecVersionService).getVersion(7L, 20L, 101L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diffsCurrentUsersProjectSpecVersions()
|
||||
{
|
||||
ProjectSpecDiffResult diff = new ProjectSpecDiffResult();
|
||||
when(projectSpecVersionDiffService.diff(7L, 20L, 101L, 102L)).thenReturn(diff);
|
||||
|
||||
AjaxResult result = controller.diffSpecVersions(20L, 101L, 102L);
|
||||
|
||||
assertEquals(diff, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecVersionDiffService).diff(7L, 20L, 101L, 102L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatesCurrentUsersProjectSpecVersion()
|
||||
{
|
||||
ProjectSpecGenerationResult generation = new ProjectSpecGenerationResult();
|
||||
generation.setSpecVersionId(101L);
|
||||
when(projectSpecGenerationService.generate(7L, 20L, 101L)).thenReturn(generation);
|
||||
|
||||
AjaxResult result = controller.generateSpecVersion(20L, 101L);
|
||||
|
||||
assertEquals(generation, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecGenerationService).generate(7L, 20L, 101L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rollsBackCurrentUsersProjectSpecVersion()
|
||||
{
|
||||
ProjectSpecRollbackRequest request = new ProjectSpecRollbackRequest();
|
||||
request.setChangeSummary("Restore stable design");
|
||||
ProjectSpecVersionView version = version(103L, 6);
|
||||
when(projectSpecVersionService.rollback(7L, 20L, 101L, "Restore stable design"))
|
||||
.thenReturn(version);
|
||||
|
||||
AjaxResult result = controller.rollbackSpecVersion(20L, 101L, request);
|
||||
|
||||
assertEquals(version, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecVersionService).rollback(7L, 20L, 101L, "Restore stable design");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listsCurrentUsersPersistedGenerationRuns()
|
||||
{
|
||||
ProjectSpecGenerationResult run = new ProjectSpecGenerationResult();
|
||||
run.setGenerationRunId(301L);
|
||||
when(projectSpecGenerationRunService.list(7L, 20L))
|
||||
.thenReturn(Collections.singletonList(run));
|
||||
|
||||
AjaxResult result = controller.listGenerationRuns(20L);
|
||||
|
||||
assertEquals(Collections.singletonList(run), result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecGenerationRunService).list(7L, 20L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readsCurrentUsersPersistedGenerationRun()
|
||||
{
|
||||
ProjectSpecGenerationResult run = new ProjectSpecGenerationResult();
|
||||
run.setGenerationRunId(301L);
|
||||
when(projectSpecGenerationRunService.get(7L, 20L, 301L)).thenReturn(run);
|
||||
|
||||
AjaxResult result = controller.getGenerationRun(20L, 301L);
|
||||
|
||||
assertEquals(run, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecGenerationRunService).get(7L, 20L, 301L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diffsCurrentUsersPersistedGenerationRuns()
|
||||
{
|
||||
ProjectSpecGenerationRunDiffResult diff = new ProjectSpecGenerationRunDiffResult();
|
||||
when(projectSpecGenerationRunService.diff(7L, 20L, 301L, 302L)).thenReturn(diff);
|
||||
|
||||
AjaxResult result = controller.diffGenerationRuns(20L, 301L, 302L);
|
||||
|
||||
assertEquals(diff, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectSpecGenerationRunService).diff(7L, 20L, 301L, 302L);
|
||||
}
|
||||
|
||||
private ProjectSpecVersionView version(Long id, int versionNo)
|
||||
{
|
||||
ProjectSpecVersionView version = new ProjectSpecVersionView();
|
||||
version.setSpecVersionId(id);
|
||||
version.setVersionNo(versionNo);
|
||||
return version;
|
||||
}
|
||||
|
||||
private LoginUser loginUser()
|
||||
{
|
||||
SysUser user = new SysUser();
|
||||
user.setUserName("front");
|
||||
user.setPassword("password");
|
||||
LoginUser loginUser = new LoginUser(7L, 1L, user, Collections.<String>emptySet());
|
||||
loginUser.setLoginType("front");
|
||||
return loginUser;
|
||||
}
|
||||
}
|
||||
@@ -3,23 +3,55 @@ package com.ruoyi.web.controller.front;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.generator.domain.TemplateBundle;
|
||||
import com.ruoyi.generator.domain.front.FrontProject;
|
||||
import com.ruoyi.generator.domain.front.FrontProjectGeneration;
|
||||
import com.ruoyi.generator.domain.front.dto.GenerateCodeAnalysisRequest;
|
||||
import com.ruoyi.generator.domain.front.dto.AiGenerationTaskOverview;
|
||||
import com.ruoyi.generator.domain.front.dto.AiGenerationTaskHistoryItem;
|
||||
import com.ruoyi.generator.domain.front.dto.block.BusinessBlockDefinition;
|
||||
import com.ruoyi.generator.service.ITemplateBundleService;
|
||||
import com.ruoyi.generator.service.front.BusinessBlockRegistryService;
|
||||
import com.ruoyi.generator.service.front.IAiGenerateService;
|
||||
import com.ruoyi.generator.service.front.IAiGenerationTaskService;
|
||||
import com.ruoyi.generator.service.front.IDeepSeekClient;
|
||||
import com.ruoyi.generator.domain.front.dto.ProjectRunPreviewStatus;
|
||||
import com.ruoyi.generator.domain.front.dto.ProjectDeliveryReadiness;
|
||||
import com.ruoyi.generator.domain.front.dto.ProjectQualityRunSummary;
|
||||
import com.ruoyi.generator.factory.quality.ProjectQualityRunService;
|
||||
import com.ruoyi.generator.factory.artifact.CertifiedArtifactService;
|
||||
import com.ruoyi.generator.factory.artifact.model.ProjectCertifiedArtifact;
|
||||
import com.ruoyi.generator.factory.acceptance.FactoryV1AcceptanceService;
|
||||
import com.ruoyi.generator.factory.acceptance.model.FactoryV1AcceptanceRun;
|
||||
import com.ruoyi.generator.service.front.IFrontProjectPreviewService;
|
||||
import com.ruoyi.generator.service.front.IFrontProjectService;
|
||||
import com.ruoyi.generator.service.front.IFrontProjectRunPreviewService;
|
||||
import com.ruoyi.generator.service.front.IProjectDeliveryReadinessService;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -33,6 +65,26 @@ public class FrontProjectControllerRunPreviewTest
|
||||
private BusinessBlockRegistryService businessBlockRegistryService;
|
||||
@Mock
|
||||
private IFrontProjectService frontProjectService;
|
||||
@Mock
|
||||
private IFrontProjectPreviewService frontProjectPreviewService;
|
||||
@Mock
|
||||
private ITemplateBundleService templateBundleService;
|
||||
@Mock
|
||||
private IAiGenerationTaskService aiGenerationTaskService;
|
||||
@Mock
|
||||
private IAiGenerateService aiGenerateService;
|
||||
@Mock
|
||||
private IProjectDeliveryReadinessService projectDeliveryReadinessService;
|
||||
@Mock
|
||||
private ProjectQualityRunService projectQualityRunService;
|
||||
@Mock
|
||||
private CertifiedArtifactService certifiedArtifactService;
|
||||
@Mock
|
||||
private FactoryV1AcceptanceService factoryV1AcceptanceService;
|
||||
@Mock
|
||||
private HttpServletResponse httpServletResponse;
|
||||
@Mock
|
||||
private ServletOutputStream servletOutputStream;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
@@ -42,15 +94,142 @@ public class FrontProjectControllerRunPreviewTest
|
||||
setField("frontProjectRunPreviewService", frontProjectRunPreviewService);
|
||||
setField("businessBlockRegistryService", businessBlockRegistryService);
|
||||
setField("frontProjectService", frontProjectService);
|
||||
setField("frontProjectPreviewService", frontProjectPreviewService);
|
||||
setField("templateBundleService", templateBundleService);
|
||||
setField("aiGenerationTaskService", aiGenerationTaskService);
|
||||
setField("aiGenerateService", aiGenerateService);
|
||||
setField("projectDeliveryReadinessService", projectDeliveryReadinessService);
|
||||
setField("projectQualityRunService", projectQualityRunService);
|
||||
setField("certifiedArtifactService", certifiedArtifactService);
|
||||
setField("factoryV1AcceptanceService", factoryV1AcceptanceService);
|
||||
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(loginUser(), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deliveryReadinessDelegatesToCurrentFrontUser()
|
||||
{
|
||||
ProjectDeliveryReadiness readiness = new ProjectDeliveryReadiness();
|
||||
readiness.setProjectId(20L);
|
||||
readiness.setStatus(ProjectDeliveryReadiness.STATUS_SOURCE_READY);
|
||||
when(projectDeliveryReadinessService.get(7L, 20L)).thenReturn(readiness);
|
||||
|
||||
AjaxResult result = controller.deliveryReadiness(20L);
|
||||
|
||||
assertEquals(readiness, result.get(AjaxResult.DATA_TAG));
|
||||
verify(projectDeliveryReadinessService).get(7L, 20L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void certifiedDownloadChecksGateBeforeWritingArchive() throws Exception
|
||||
{
|
||||
FrontProject project = new FrontProject();
|
||||
project.setProjectId(20L);
|
||||
project.setProjectName("客户中心");
|
||||
ProjectDeliveryReadiness readiness = new ProjectDeliveryReadiness();
|
||||
readiness.setCertificationLevel(ProjectDeliveryReadiness.CERTIFICATION_BASELINE);
|
||||
readiness.setCertifiedDownloadAllowed(true);
|
||||
readiness.setGenerationRunId(41L);
|
||||
ProjectQualityRunSummary qualityRun = new ProjectQualityRunSummary();
|
||||
qualityRun.setQualityRunId(51L);
|
||||
readiness.setQualityRun(qualityRun);
|
||||
ProjectCertifiedArtifact artifact = new ProjectCertifiedArtifact();
|
||||
artifact.setArtifactId(61L);
|
||||
artifact.setQualityRunId(51L);
|
||||
artifact.setProjectId(20L);
|
||||
artifact.setUserId(7L);
|
||||
artifact.setGenerationRunId(41L);
|
||||
artifact.setStatus(ProjectCertifiedArtifact.STATUS_READY);
|
||||
artifact.setZipSha256("zip-hash");
|
||||
artifact.setReportSha256("report-hash");
|
||||
when(projectDeliveryReadinessService.assertDownloadAllowed(7L, 20L, "CERTIFIED"))
|
||||
.thenReturn(readiness);
|
||||
when(frontProjectService.getProject(7L, 20L)).thenReturn(project);
|
||||
when(certifiedArtifactService.getReady(7L, 20L, 51L)).thenReturn(artifact);
|
||||
when(certifiedArtifactService.read(artifact)).thenReturn(new byte[] { 1, 2, 3 });
|
||||
when(httpServletResponse.getOutputStream()).thenReturn(servletOutputStream);
|
||||
|
||||
controller.download(20L, null, "CERTIFIED", httpServletResponse);
|
||||
|
||||
verify(projectDeliveryReadinessService).assertDownloadAllowed(7L, 20L, "CERTIFIED");
|
||||
verify(certifiedArtifactService).read(artifact);
|
||||
verify(frontProjectPreviewService, never()).downloadAll(7L, 20L);
|
||||
verify(httpServletResponse).addHeader("X-Factory-Artifact-Type", "CERTIFIED");
|
||||
verify(httpServletResponse).addHeader("X-Factory-Certification-Level", "BASELINE");
|
||||
verify(httpServletResponse).addHeader("X-Factory-Artifact-Id", "61");
|
||||
verify(httpServletResponse).addHeader("X-Factory-Zip-SHA256", "zip-hash");
|
||||
verify(httpServletResponse).addHeader("X-Factory-Report-SHA256", "report-hash");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void qualityRunEndpointsUseCurrentFrontUser()
|
||||
{
|
||||
ProjectQualityRunSummary summary = new ProjectQualityRunSummary();
|
||||
summary.setQualityRunId(301L);
|
||||
summary.setStatus("QUEUED");
|
||||
when(projectQualityRunService.createSummary(7L, 20L)).thenReturn(summary);
|
||||
when(projectQualityRunService.listSummaries(7L, 20L))
|
||||
.thenReturn(Collections.singletonList(summary));
|
||||
when(projectQualityRunService.latestSummary(7L, 20L)).thenReturn(summary);
|
||||
|
||||
assertEquals(summary, controller.createQualityRun(20L).get(AjaxResult.DATA_TAG));
|
||||
assertEquals(Collections.singletonList(summary),
|
||||
controller.qualityRuns(20L).get(AjaxResult.DATA_TAG));
|
||||
assertEquals(summary, controller.latestQualityRun(20L).get(AjaxResult.DATA_TAG));
|
||||
verify(projectQualityRunService).createSummary(7L, 20L);
|
||||
verify(projectQualityRunService).listSummaries(7L, 20L);
|
||||
verify(projectQualityRunService).latestSummary(7L, 20L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void v1AcceptanceEndpointsUseCurrentFrontUser()
|
||||
{
|
||||
FactoryV1AcceptanceRun run = new FactoryV1AcceptanceRun();
|
||||
run.setAcceptanceRunId(71L);
|
||||
run.setStatus(FactoryV1AcceptanceRun.STATUS_PASSED);
|
||||
when(factoryV1AcceptanceService.start(7L, 20L)).thenReturn(run);
|
||||
when(factoryV1AcceptanceService.latest(7L, 20L)).thenReturn(run);
|
||||
when(factoryV1AcceptanceService.list(7L, 20L)).thenReturn(Collections.singletonList(run));
|
||||
|
||||
assertEquals(run, controller.startV1Acceptance(20L).get(AjaxResult.DATA_TAG));
|
||||
assertEquals(run, controller.latestV1Acceptance(20L).get(AjaxResult.DATA_TAG));
|
||||
assertEquals(Collections.singletonList(run), controller.v1Acceptances(20L).get(AjaxResult.DATA_TAG));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown()
|
||||
{
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oneClickTaskOverviewDelegatesToCurrentFrontUser()
|
||||
{
|
||||
AiGenerationTaskOverview overview = new AiGenerationTaskOverview();
|
||||
overview.setTaskId(88L);
|
||||
when(aiGenerationTaskService.listOneClickTaskOverview(7L))
|
||||
.thenReturn(Collections.singletonList(overview));
|
||||
|
||||
AjaxResult result = controller.oneClickTaskOverview();
|
||||
|
||||
assertEquals(Collections.singletonList(overview),
|
||||
result.get(AjaxResult.DATA_TAG));
|
||||
verify(aiGenerationTaskService).listOneClickTaskOverview(7L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oneClickTaskHistoryDelegatesToCurrentFrontUser()
|
||||
{
|
||||
AiGenerationTaskHistoryItem item = new AiGenerationTaskHistoryItem();
|
||||
item.setTaskId(99L);
|
||||
when(aiGenerationTaskService.listOneClickTaskHistory(7L, 20L))
|
||||
.thenReturn(Collections.singletonList(item));
|
||||
|
||||
AjaxResult result = controller.oneClickTaskHistory(20L);
|
||||
|
||||
assertEquals(Collections.singletonList(item), result.get(AjaxResult.DATA_TAG));
|
||||
verify(aiGenerationTaskService).listOneClickTaskHistory(7L, 20L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startRunPreviewDelegatesToCurrentFrontUser()
|
||||
{
|
||||
@@ -102,6 +281,145 @@ public class FrontProjectControllerRunPreviewTest
|
||||
verify(businessBlockRegistryService).listDefinitions("admin");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void codeTemplateOptionsReturnsEnabledBundles()
|
||||
{
|
||||
TemplateBundle qing = new TemplateBundle();
|
||||
qing.setBundleCode("qing");
|
||||
qing.setBundleName("Qing Default Template");
|
||||
when(templateBundleService.listEnabledBundles()).thenReturn(Collections.singletonList(qing));
|
||||
|
||||
AjaxResult result = controller.codeTemplates();
|
||||
|
||||
assertEquals(Collections.singletonList(qing), result.get(AjaxResult.DATA_TAG));
|
||||
verify(templateBundleService).listEnabledBundles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectTemplateTypesDelegatesToPreviewService()
|
||||
{
|
||||
when(frontProjectPreviewService.getSupportedTemplateTypes(7L, 20L))
|
||||
.thenReturn(Arrays.asList("backend", "sql"));
|
||||
|
||||
AjaxResult result = controller.templateTypes(20L);
|
||||
|
||||
assertEquals(Arrays.asList("backend", "sql"), result.get(AjaxResult.DATA_TAG));
|
||||
verify(frontProjectPreviewService).getSupportedTemplateTypes(7L, 20L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void previewGeneratesOnlySupportedTemplateTypes()
|
||||
{
|
||||
FrontProject project = new FrontProject();
|
||||
project.setFrontendEnabled("1");
|
||||
when(frontProjectService.getProject(7L, 20L)).thenReturn(project);
|
||||
when(frontProjectPreviewService.getSupportedTemplateTypes(7L, 20L))
|
||||
.thenReturn(Arrays.asList("backend", "sql"));
|
||||
when(frontProjectPreviewService.markPreviewReady(7L, 20L)).thenReturn(1);
|
||||
|
||||
controller.preview(20L);
|
||||
|
||||
verify(frontProjectPreviewService).getSupportedTemplateTypes(7L, 20L);
|
||||
verify(frontProjectPreviewService).getStructure(7L, 20L, "backend");
|
||||
verify(frontProjectPreviewService).getStructure(7L, 20L, "sql");
|
||||
verify(frontProjectPreviewService, never()).getStructure(7L, 20L, "frontend");
|
||||
verify(frontProjectPreviewService, never()).getStructure(7L, 20L, "admin_frontend");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createCodeAnalysisRequiresSourcePreviewReady()
|
||||
{
|
||||
FrontProject project = new FrontProject();
|
||||
project.setPreviewStatus("0");
|
||||
when(frontProjectService.getProject(7L, 20L)).thenReturn(project);
|
||||
|
||||
try
|
||||
{
|
||||
controller.createCodeAnalysis(20L, null);
|
||||
fail("Expected ServiceException");
|
||||
}
|
||||
catch (ServiceException e)
|
||||
{
|
||||
assertEquals("请先生成源码预览后再进行代码解读", e.getMessage());
|
||||
}
|
||||
verify(aiGenerationTaskService, never()).createTask(any(Long.class), any(Long.class), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void continueCodeAnalysisRequiresSourcePreviewReady()
|
||||
{
|
||||
FrontProject project = new FrontProject();
|
||||
project.setPreviewStatus("0");
|
||||
when(frontProjectService.getProject(7L, 20L)).thenReturn(project);
|
||||
|
||||
try
|
||||
{
|
||||
controller.continueCodeAnalysis(20L, 99L, null);
|
||||
fail("Expected ServiceException");
|
||||
}
|
||||
catch (ServiceException e)
|
||||
{
|
||||
assertEquals("请先生成源码预览后再进行代码解读", e.getMessage());
|
||||
}
|
||||
verify(aiGenerationTaskService, never()).getGeneration(any(Long.class), any(Long.class), any(Long.class));
|
||||
verify(aiGenerationTaskService, never()).createTask(any(Long.class), any(Long.class), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void streamCodeAnalysisWritesChunks() throws Exception
|
||||
{
|
||||
FrontProject project = new FrontProject();
|
||||
project.setPreviewStatus("1");
|
||||
when(frontProjectService.getProject(7L, 20L)).thenReturn(project);
|
||||
doAnswer(invocation ->
|
||||
{
|
||||
IDeepSeekClient.StreamHandler handler = invocation.getArgument(3);
|
||||
handler.onContent("# 报告\n\n");
|
||||
handler.onContent("正文");
|
||||
return null;
|
||||
}).when(aiGenerateService).streamCodeAnalysis(eq(7L), eq(20L), any(GenerateCodeAnalysisRequest.class),
|
||||
any(IDeepSeekClient.StreamHandler.class));
|
||||
|
||||
StreamingResponseBody body = controller.streamCodeAnalysis(20L, new GenerateCodeAnalysisRequest());
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
body.writeTo(output);
|
||||
|
||||
assertEquals("# 报告\n\n正文", new String(output.toByteArray(), StandardCharsets.UTF_8));
|
||||
verify(aiGenerateService).streamCodeAnalysis(eq(7L), eq(20L), any(GenerateCodeAnalysisRequest.class),
|
||||
any(IDeepSeekClient.StreamHandler.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void continueCodeAnalysisStreamUsesCurrentEditedMarkdown() throws Exception
|
||||
{
|
||||
FrontProject project = new FrontProject();
|
||||
project.setPreviewStatus("1");
|
||||
when(frontProjectService.getProject(7L, 20L)).thenReturn(project);
|
||||
FrontProjectGeneration previous = new FrontProjectGeneration();
|
||||
previous.setGenerateType("code_analysis");
|
||||
previous.setResponsePayload("{\"markdown\":\"old\"}");
|
||||
when(aiGenerationTaskService.getGeneration(7L, 20L, 99L)).thenReturn(previous);
|
||||
doAnswer(invocation ->
|
||||
{
|
||||
IDeepSeekClient.StreamHandler handler = invocation.getArgument(3);
|
||||
handler.onContent("续写");
|
||||
return null;
|
||||
}).when(aiGenerateService).streamCodeAnalysis(eq(7L), eq(20L), any(GenerateCodeAnalysisRequest.class),
|
||||
any(IDeepSeekClient.StreamHandler.class));
|
||||
GenerateCodeAnalysisRequest request = new GenerateCodeAnalysisRequest();
|
||||
request.setPreviousMarkdown("edited");
|
||||
|
||||
StreamingResponseBody body = controller.continueCodeAnalysisStream(20L, 99L, request);
|
||||
body.writeTo(new ByteArrayOutputStream());
|
||||
|
||||
ArgumentCaptor<GenerateCodeAnalysisRequest> requestCaptor = ArgumentCaptor.forClass(GenerateCodeAnalysisRequest.class);
|
||||
verify(aiGenerateService).streamCodeAnalysis(eq(7L), eq(20L), requestCaptor.capture(),
|
||||
any(IDeepSeekClient.StreamHandler.class));
|
||||
assertEquals("continue", requestCaptor.getValue().getMode());
|
||||
assertEquals(Long.valueOf(99L), requestCaptor.getValue().getPreviousGenerationId());
|
||||
assertEquals("edited", requestCaptor.getValue().getPreviousMarkdown());
|
||||
}
|
||||
|
||||
private ProjectRunPreviewStatus status(String value)
|
||||
{
|
||||
ProjectRunPreviewStatus status = new ProjectRunPreviewStatus();
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.ruoyi.web.exception;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.junit.Test;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.framework.web.exception.GlobalExceptionHandler;
|
||||
|
||||
public class GlobalExceptionHandlerTest
|
||||
{
|
||||
@Test
|
||||
public void maxUploadSizeExceededReturnsReadableMessage()
|
||||
{
|
||||
GlobalExceptionHandler handler = new GlobalExceptionHandler();
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
when(request.getRequestURI()).thenReturn("/generator/templateImport/analyze");
|
||||
|
||||
AjaxResult result = handler.handleMaxUploadSizeExceededException(
|
||||
new MaxUploadSizeExceededException(100L * 1024L * 1024L), request);
|
||||
|
||||
assertEquals("上传文件大小不能超过 100MB", result.get(AjaxResult.MSG_TAG));
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,14 @@ import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.framework.web.service.TokenService;
|
||||
import com.ruoyi.generator.domain.front.FrontUser;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontPasswordUpdateBody;
|
||||
import com.ruoyi.generator.domain.front.dto.FrontProfileUpdateBody;
|
||||
import com.ruoyi.generator.service.front.IFrontUserService;
|
||||
import com.ruoyi.web.controller.front.FrontAuthController;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@@ -18,6 +21,7 @@ import java.lang.reflect.Field;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -196,6 +200,93 @@ public class FrontAuthServiceTest
|
||||
assertNull(profile.getPassword());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateProfilePersistsEditableFieldsAndReturnsPasswordFreeProfile()
|
||||
{
|
||||
FrontProfileUpdateBody body = new FrontProfileUpdateBody();
|
||||
body.setNickname(" Demo User ");
|
||||
body.setEmail(" demo@example.com ");
|
||||
body.setPhone(" 13800138000 ");
|
||||
|
||||
FrontUser refreshed = new FrontUser();
|
||||
refreshed.setUserId(100L);
|
||||
refreshed.setUsername("demo");
|
||||
refreshed.setNickname("Demo User");
|
||||
refreshed.setEmail("demo@example.com");
|
||||
refreshed.setPhone("13800138000");
|
||||
refreshed.setPassword("encoded-password");
|
||||
|
||||
when(frontUserService.updateProfile(any(FrontUser.class))).thenReturn(1);
|
||||
when(frontUserService.selectById(100L)).thenReturn(refreshed);
|
||||
|
||||
FrontUser profile = frontAuthService.updateProfile(100L, body);
|
||||
|
||||
ArgumentCaptor<FrontUser> captor = ArgumentCaptor.forClass(FrontUser.class);
|
||||
verify(frontUserService).updateProfile(captor.capture());
|
||||
FrontUser saved = captor.getValue();
|
||||
assertEquals(Long.valueOf(100L), saved.getUserId());
|
||||
assertEquals("Demo User", saved.getNickname());
|
||||
assertEquals("demo@example.com", saved.getEmail());
|
||||
assertEquals("13800138000", saved.getPhone());
|
||||
assertNull(saved.getUsername());
|
||||
assertNull(saved.getPassword());
|
||||
assertEquals("Demo User", profile.getNickname());
|
||||
assertNull(profile.getPassword());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePasswordRejectsWrongOldPassword()
|
||||
{
|
||||
FrontUser user = new FrontUser();
|
||||
user.setUserId(100L);
|
||||
user.setUsername("demo");
|
||||
user.setPassword(SecurityUtils.encryptPassword("old123"));
|
||||
|
||||
FrontPasswordUpdateBody body = new FrontPasswordUpdateBody();
|
||||
body.setOldPassword("wrong");
|
||||
body.setNewPassword("new123");
|
||||
body.setConfirmPassword("new123");
|
||||
|
||||
when(frontUserService.selectById(100L)).thenReturn(user);
|
||||
|
||||
try
|
||||
{
|
||||
frontAuthService.updatePassword(100L, body);
|
||||
fail("Expected ServiceException");
|
||||
}
|
||||
catch (ServiceException e)
|
||||
{
|
||||
assertEquals("旧密码错误", e.getMessage());
|
||||
}
|
||||
|
||||
verify(frontUserService, never()).updatePassword(any(FrontUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePasswordEncryptsAcceptedNewPassword()
|
||||
{
|
||||
FrontUser user = new FrontUser();
|
||||
user.setUserId(100L);
|
||||
user.setUsername("demo");
|
||||
user.setPassword(SecurityUtils.encryptPassword("old123"));
|
||||
|
||||
FrontPasswordUpdateBody body = new FrontPasswordUpdateBody();
|
||||
body.setOldPassword("old123");
|
||||
body.setNewPassword("new123");
|
||||
body.setConfirmPassword("new123");
|
||||
|
||||
when(frontUserService.selectById(100L)).thenReturn(user);
|
||||
when(frontUserService.updatePassword(any(FrontUser.class))).thenReturn(1);
|
||||
|
||||
frontAuthService.updatePassword(100L, body);
|
||||
|
||||
ArgumentCaptor<FrontUser> captor = ArgumentCaptor.forClass(FrontUser.class);
|
||||
verify(frontUserService).updatePassword(captor.capture());
|
||||
FrontUser saved = captor.getValue();
|
||||
assertEquals(Long.valueOf(100L), saved.getUserId());
|
||||
assertTrue(SecurityUtils.matchesPassword("new123", saved.getPassword()));
|
||||
}
|
||||
|
||||
private void setField(Object target, String name, Object value) throws Exception
|
||||
{
|
||||
Field field = target.getClass().getDeclaredField(name);
|
||||
|
||||
Reference in New Issue
Block a user