Compare commits
79 Commits
501032ef17
...
dev/dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e7004f9dc | |||
| 497421e7fe | |||
| 891527426c | |||
|
|
8f0d0953b2 | ||
| f5c3621a5d | |||
|
|
9a1a0045e0 | ||
| 6223c8e994 | |||
| 67bbee49fd | |||
| ad62ceb32a | |||
| 082afe9e94 | |||
| 49288c3a31 | |||
| 81624e36db | |||
| a526b122d1 | |||
|
|
d882b2e817 | ||
|
|
ebf6427d42 | ||
| 77fe03d361 | |||
| 7a44d67dbf | |||
| 55ce2c6c7e | |||
| a426caaca3 | |||
| 7cb7ce2836 | |||
| 8e075f1da4 | |||
|
|
0f0fde2a3e | ||
|
|
8c6389a1f6 | ||
| 652f82b6a4 | |||
| 7ca2528dcf | |||
|
|
a7800913d2 | ||
|
|
1eaec64ff4 | ||
| e603952332 | |||
| 2bc8b8ef96 | |||
| 0ce968b919 | |||
|
|
dfc9ae4db2 | ||
|
|
a3505c6d95 | ||
|
|
6db0afd515 | ||
|
|
b1e6183dd1 | ||
| 30d08356c0 | |||
| 64cc29f456 | |||
|
|
2b3e12a11c | ||
|
|
d4a4724f61 | ||
|
|
ba6e2bd24c | ||
|
|
a38895b028 | ||
|
|
69a95e66ca | ||
|
|
40518cab37 | ||
|
|
46d61cb73f | ||
| 08f20fd1fe | |||
| d7edc166b3 | |||
|
|
79ad02f66b | ||
|
|
5e261b55c7 | ||
|
|
bc92fcbaf4 | ||
|
|
c6aec917c2 | ||
| 6bc500e78f | |||
| 4c43b98c02 | |||
|
|
5bae785a9f | ||
|
|
7b619aa4cb | ||
| c93ad6daa9 | |||
| 0047be7a03 | |||
| 4ef209cfd4 | |||
| a19751b4b7 | |||
|
|
bb0e5a4263 | ||
|
|
9e9df5367d | ||
| ba8a2c52de | |||
| 39d8c7efcf | |||
|
|
401910901a | ||
|
|
3f5ce6e0e7 | ||
| 0787025151 | |||
| 08b26872ff | |||
| 5bbf1326bb | |||
|
|
c5e27cd220 | ||
|
|
112e9c3bc9 | ||
|
|
ce95cb5080 | ||
|
|
71211bfbc3 | ||
| 72ad977dcb | |||
|
|
6400e79929 | ||
|
|
dd8c72f7d7 | ||
| 13151b65f5 | |||
| 9f523d5953 | |||
| 4879cfeb60 | |||
| 9e252b16ef | |||
| e64add14af | |||
| 3beb27e491 |
@@ -559,7 +559,7 @@ public class GenerateConsumer {
|
||||
log.info("============ProcessPoseTransformResult End listening==========");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
/*@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer1(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 1");
|
||||
@@ -635,7 +635,7 @@ public class GenerateConsumer {
|
||||
@RabbitHandler
|
||||
public void getPoseTransformationResult(Message msg, Channel channel) {
|
||||
processPoseTransformResult(msg, channel);
|
||||
}
|
||||
}*/
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.designBatch}")
|
||||
// @RabbitHandler
|
||||
// public void getDesignBatchResult(Message msg, Channel channel) {
|
||||
|
||||
@@ -33,7 +33,11 @@ public enum AuthenticationOperationTypeEnum {
|
||||
*/
|
||||
UPDATE_USERINFO,
|
||||
|
||||
REGISTER;
|
||||
REGISTER,
|
||||
/**
|
||||
* Global_Award 活动验证
|
||||
*/
|
||||
GLOBAL_AWARD;
|
||||
|
||||
public static AuthenticationOperationTypeEnum of(String name) {
|
||||
return Stream.of(AuthenticationOperationTypeEnum.values()).filter(v -> v.name().equals(name)).findFirst().orElse(null);
|
||||
|
||||
@@ -59,7 +59,11 @@ public class AuthenticationFilter extends OncePerRequestFilter {
|
||||
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
|
||||
"/notification","/api/account/activateNewEmail","/api/third/party/auth/google_callback","/api/third/party/parseGoogleCredential","/api/third/party/receiveDesignResults","/api/third/party/parseWeChatCode","/api/third/party/receiveDesignParams"
|
||||
, "/api/account/schoolLogin", "/api/account/enterpriseLogin", "/api/account/organizationNameSearch",
|
||||
"/api/llm/stream"
|
||||
"/api/llm/stream",
|
||||
//GlobalAwardController
|
||||
"/api/global-award/uploads/pdf/init", "/api/global-award/uploads/pdf/chunk", "/api/global-award/uploads/pdf/complete", "/api/global-award/uploads/pdf/status",
|
||||
"/api/global-award/uploads/video/init", "/api/global-award/uploads/video/chunk", "/api/global-award/uploads/video/complete", "/api/global-award/uploads/video/status",
|
||||
"/api/global-award/contestants/save", "/api/global-award/contestants/by-email", "/api/global-award/checkEmail", "/api/global-award/checkCode"
|
||||
);
|
||||
|
||||
@Override
|
||||
|
||||
@@ -40,8 +40,8 @@ public class AccountTask {
|
||||
accountService.extendValidityForCC();
|
||||
}
|
||||
|
||||
// 每天凌晨0点执行一次
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
// 每天凌晨0点执行一次 目前已没有角色类型为4的用户
|
||||
/*@Scheduled(cron = "0 0 0 * * ?")
|
||||
public void cancelActivityBenefits() {
|
||||
// 1、查询当前所有参与了活动且过期的用户
|
||||
List<Account> accountList = accountService.getExpiredUserBySystemUser(4);
|
||||
@@ -51,7 +51,7 @@ public class AccountTask {
|
||||
log.info("参与活动的用户{} : {} 于 {} 账号有效期到期,置为游客", account.getId(), account.getUserEmail(), account.getValidEndTime());
|
||||
accountService.toVisitor(account);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// 每天检测正式用户到期情况,每天凌晨0点执行
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
@@ -92,7 +92,7 @@ public class AccountTask {
|
||||
|
||||
@Scheduled(cron = "0 5 0 * * ?")
|
||||
public void activeSubscriptionPlan() {
|
||||
subscriptionPlanService.activeSubscriptionPlan();
|
||||
subscriptionPlanService.activeSubscriptionPlan(null);
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
|
||||
@@ -41,6 +41,13 @@ public class MinioUtil {
|
||||
@Autowired
|
||||
private MinioClient minioClient;
|
||||
|
||||
/**
|
||||
* 获取MinIO客户端实例
|
||||
*/
|
||||
public MinioClient getMinioClient() {
|
||||
return minioClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* description: 判断bucket是否存在,不存在则创建
|
||||
*
|
||||
|
||||
@@ -15,16 +15,16 @@ import com.ai.da.model.vo.PersonalHomepageVO;
|
||||
import com.ai.da.service.AccountService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -139,21 +139,21 @@ public class AccountController {
|
||||
@Operation(summary = "aws状态检测")
|
||||
@GetMapping("/healthy")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public Response<Map<String,Integer>> checkStatus(){
|
||||
Map<String,Integer> returnMap = new HashMap<>();
|
||||
returnMap.put("code",200);
|
||||
public Response<Map<String, Integer>> checkStatus() {
|
||||
Map<String, Integer> returnMap = new HashMap<>();
|
||||
returnMap.put("code", 200);
|
||||
return Response.success(returnMap);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询账号到期时间")
|
||||
@PostMapping("/getExpiredTime")
|
||||
public Response<Long> getExpiredTime(){
|
||||
public Response<Long> getExpiredTime() {
|
||||
return Response.success(accountService.getExpiredTime());
|
||||
}
|
||||
|
||||
@Operation(summary = "免密登录")
|
||||
@PostMapping("/noLoginRequired")
|
||||
public Response<AccountLoginVO> noLoginRequired(@RequestBody NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request){
|
||||
public Response<AccountLoginVO> noLoginRequired(@RequestBody NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request) {
|
||||
return Response.success(accountService.noLoginRequired(noLoginRequiredDTO, request));
|
||||
}
|
||||
|
||||
@@ -191,6 +191,7 @@ public class AccountController {
|
||||
|
||||
/**
|
||||
* 参与活动 获取福利
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/* @Operation(summary = "参与活动 获取福利")
|
||||
@@ -201,7 +202,7 @@ public class AccountController {
|
||||
|
||||
@Operation(summary = "将用户账号过期时间设置为过期当天的23:59:59")
|
||||
@GetMapping("/setUserValidToDayEnd")
|
||||
public Response<List<Long>> setUserValidToDayEnd(){
|
||||
public Response<List<Long>> setUserValidToDayEnd() {
|
||||
return Response.success(accountService.setUserValidToDayEnd());
|
||||
}
|
||||
|
||||
@@ -223,19 +224,19 @@ public class AccountController {
|
||||
|
||||
@Operation(summary = "获取个人主页信息")
|
||||
@GetMapping("/personalHomepage")
|
||||
public Response<PersonalHomepageVO> getPersonalHomepage(@RequestParam("id") Long id){
|
||||
public Response<PersonalHomepageVO> getPersonalHomepage(@RequestParam("id") Long id) {
|
||||
return Response.success(accountService.getPersonalHomepage(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "getUsernameModifyTimes")
|
||||
@GetMapping("/getNicknameModifyTimes")
|
||||
public Response<Long> getNicknameModifyTimes(){
|
||||
public Response<Long> getNicknameModifyTimes() {
|
||||
return Response.success(accountService.getNicknameModifyTimes());
|
||||
}
|
||||
|
||||
@Operation(summary = "editUserName")
|
||||
@GetMapping("/editUserName")
|
||||
public Response<String> editUserName(@RequestParam("newUserName") String newUserName){
|
||||
public Response<String> editUserName(@RequestParam("newUserName") String newUserName) {
|
||||
accountService.editUserName(newUserName);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public class ConvenientInquiryController {
|
||||
@GetMapping("/recentNewUserChart")
|
||||
public Response<Map<String, Object>> recentNewUserChart(@Parameter(description = "startTime") @RequestParam @Nullable String startTime,
|
||||
@Parameter(description = "endTime") @RequestParam @Nullable String endTime,
|
||||
@Parameter(description = "userType") @RequestParam Integer userType) {
|
||||
@Parameter(description = "userType") @RequestParam @Nullable Integer userType) {
|
||||
return Response.success(convenientInquiryService.recentNewUserChart(startTime, endTime, userType));
|
||||
}
|
||||
|
||||
@@ -179,8 +179,10 @@ public class ConvenientInquiryController {
|
||||
|
||||
@Operation(summary = "获取所有用户id")
|
||||
@GetMapping("/getAllUserId")
|
||||
public Response<List<Map<String, Object>>> getAllUsrIdList() {
|
||||
return Response.success(convenientInquiryService.getAllUserIdList());
|
||||
public Response<IPage<Map<String, Object>>> getAllUserIdList(@Parameter(description = "page") @RequestParam Integer page,
|
||||
@Parameter(description = "size") @RequestParam Integer size,
|
||||
@Parameter(description = "email 模糊查询") @RequestParam(required = false) String email) {
|
||||
return Response.success(convenientInquiryService.getAllUserIdList(page, size, email));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有交易信息")
|
||||
|
||||
168
src/main/java/com/ai/da/controller/GlobalAwardController.java
Normal file
168
src/main/java/com/ai/da/controller/GlobalAwardController.java
Normal file
@@ -0,0 +1,168 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.dto.ContestantDTO;
|
||||
import com.ai.da.model.vo.CheckOTPVO;
|
||||
import com.ai.da.service.GlobalAwardService;
|
||||
import com.ai.da.service.upload.UploadService;
|
||||
import com.ai.da.service.upload.UploadTask;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/global-award")
|
||||
@Api(tags = "全球奖项API", description = "全球奖项大赛管理和文件上传")
|
||||
public class GlobalAwardController {
|
||||
|
||||
@Resource
|
||||
private GlobalAwardService globalAwardService;
|
||||
|
||||
@Resource
|
||||
private UploadService uploadService;
|
||||
|
||||
// @PostMapping("/uploads/pdf")
|
||||
// public Response<String> uploadPdf(@RequestParam("file") MultipartFile file,
|
||||
// @RequestParam(value = "email", required = false) String email) throws Exception {
|
||||
// return Response.success(globalAwardService.uploadPdf(file, email));
|
||||
// }
|
||||
//
|
||||
// @PostMapping("/uploads/video")
|
||||
// public Response<String> uploadVideo(@RequestParam("file") MultipartFile file,
|
||||
// @RequestParam(value = "email", required = false) String email) throws Exception {
|
||||
// return Response.success(globalAwardService.uploadVideo(file, email));
|
||||
// }
|
||||
|
||||
// ===== 新增分片上传接口 =====
|
||||
|
||||
// ===== PDF分片上传接口 =====
|
||||
|
||||
/** 初始化PDF上传任务 */
|
||||
@PostMapping("/uploads/pdf/init")
|
||||
@ApiOperation(value = "初始化PDF上传", notes = "创建新的PDF上传任务并返回上传参数")
|
||||
public Response<UploadInitResponse> initPdfUpload(@ApiParam(value = "PDF上传初始化请求", required = true) @RequestBody UploadInitRequest request) {
|
||||
UploadTask uploadTask = uploadService.initPdfUpload(request);
|
||||
return Response.success(UploadInitResponse.builder()
|
||||
.uploadId(uploadTask.getUploadId())
|
||||
.chunkSize(uploadTask.getChunkSize())
|
||||
.totalChunks(uploadTask.getTotalChunks())
|
||||
.expiresAt(uploadTask.getExpiresAt())
|
||||
.build());
|
||||
}
|
||||
|
||||
/** 上传PDF分片 */
|
||||
@PostMapping("/uploads/pdf/chunk")
|
||||
@ApiOperation(value = "上传PDF分片", notes = "上传PDF文件的单个分片")
|
||||
public Response<UploadChunkResponse> uploadPdfChunk(
|
||||
@ApiParam(value = "PDF文件分片", required = true) @RequestParam("chunk") MultipartFile chunk,
|
||||
@ApiParam(value = "上传任务ID", required = true) @RequestParam("uploadId") String uploadId,
|
||||
@ApiParam(value = "分片索引(从0开始)", required = true) @RequestParam("chunkIndex") int chunkIndex,
|
||||
@ApiParam(value = "分片总数", required = true) @RequestParam("totalChunks") int totalChunks) {
|
||||
|
||||
UploadChunkResponse uploadChunkResponse = uploadService.uploadPdfChunk(uploadId, chunk, chunkIndex, totalChunks);
|
||||
return Response.success(uploadChunkResponse);
|
||||
}
|
||||
|
||||
/** 完成PDF上传 */
|
||||
@PostMapping("/uploads/pdf/complete")
|
||||
@ApiOperation(value = "完成PDF上传", notes = "完成PDF上传并合并所有分片")
|
||||
public Response<UploadCompleteResponse> completePdfUpload(@ApiParam(value = "PDF上传完成请求", required = true) @RequestBody UploadCompleteRequest request) {
|
||||
UploadCompleteResponse uploadCompleteResponse = uploadService.completePdfUpload(
|
||||
request.getUploadId(),
|
||||
request.getFileName(),
|
||||
request.getTotalSize(),
|
||||
request.getEmail(),
|
||||
request.getSecureToken());
|
||||
return Response.success(uploadCompleteResponse);
|
||||
}
|
||||
|
||||
/** 查询PDF上传状态 */
|
||||
@GetMapping("/uploads/pdf/status/{uploadId}")
|
||||
@ApiOperation(value = "查询PDF上传状态", notes = "获取PDF上传任务的当前状态")
|
||||
public Response<UploadStatusResponse> getPdfUploadStatus(@ApiParam(value = "上传任务ID", required = true) @PathVariable String uploadId) {
|
||||
UploadStatusResponse pdfUploadStatus = uploadService.getPdfUploadStatus(uploadId);
|
||||
return Response.success(pdfUploadStatus);
|
||||
}
|
||||
|
||||
// ===== 视频分片上传接口 =====
|
||||
|
||||
/** 初始化视频上传任务 */
|
||||
@PostMapping("/uploads/video/init")
|
||||
@ApiOperation(value = "初始化视频上传", notes = "创建新的视频上传任务并返回上传参数")
|
||||
public Response<UploadInitResponse> initVideoUpload(@ApiParam(value = "视频上传初始化请求", required = true) @RequestBody UploadInitRequest request) {
|
||||
UploadTask uploadTask = uploadService.initVideoUpload(request);
|
||||
return Response.success(UploadInitResponse.builder()
|
||||
.uploadId(uploadTask.getUploadId())
|
||||
.chunkSize(uploadTask.getChunkSize())
|
||||
.totalChunks(uploadTask.getTotalChunks())
|
||||
.expiresAt(uploadTask.getExpiresAt())
|
||||
.build());
|
||||
}
|
||||
|
||||
/** 上传视频分片 */
|
||||
@PostMapping("/uploads/video/chunk")
|
||||
@ApiOperation(value = "上传视频分片", notes = "上传视频文件的单个分片")
|
||||
public Response<UploadChunkResponse> uploadVideoChunk(
|
||||
@ApiParam(value = "视频文件分片", required = true) @RequestParam("chunk") MultipartFile chunk,
|
||||
@ApiParam(value = "上传任务ID", required = true) @RequestParam("uploadId") String uploadId,
|
||||
@ApiParam(value = "分片索引(从0开始)", required = true) @RequestParam("chunkIndex") int chunkIndex,
|
||||
@ApiParam(value = "分片总数", required = true) @RequestParam("totalChunks") int totalChunks) {
|
||||
|
||||
UploadChunkResponse uploadChunkResponse = uploadService.uploadVideoChunk(uploadId, chunk, chunkIndex, totalChunks);
|
||||
return Response.success(uploadChunkResponse);
|
||||
}
|
||||
|
||||
/** 完成视频上传 */
|
||||
@PostMapping("/uploads/video/complete")
|
||||
@ApiOperation(value = "完成视频上传", notes = "完成视频上传并合并所有分片")
|
||||
public Response<UploadCompleteResponse> completeVideoUpload(@ApiParam(value = "视频上传完成请求", required = true) @RequestBody UploadCompleteRequest request) {
|
||||
UploadCompleteResponse uploadCompleteResponse = uploadService.completeVideoUpload(
|
||||
request.getUploadId(),
|
||||
request.getFileName(),
|
||||
request.getTotalSize(),
|
||||
request.getEmail(),
|
||||
request.getSecureToken());
|
||||
return Response.success(uploadCompleteResponse);
|
||||
}
|
||||
|
||||
/** 查询视频上传状态 */
|
||||
@GetMapping("/uploads/video/status/{uploadId}")
|
||||
@ApiOperation(value = "查询视频上传状态", notes = "获取视频上传任务的当前状态")
|
||||
public Response<UploadStatusResponse> getVideoUploadStatus(@ApiParam(value = "上传任务ID", required = true) @PathVariable String uploadId) {
|
||||
UploadStatusResponse videoUploadStatus = uploadService.getVideoUploadStatus(uploadId);
|
||||
return Response.success(videoUploadStatus);
|
||||
}
|
||||
|
||||
@PostMapping("/contestants/save")
|
||||
@ApiOperation(value = "保存参赛者信息", notes = "保存或更新参赛者信息及已上传的文件")
|
||||
public Response<Map<String,Object>> submit(@ApiParam(value = "参赛者信息", required = true) @RequestBody ContestantDTO request) {
|
||||
return Response.success(globalAwardService.saveContestant(request));
|
||||
}
|
||||
|
||||
@GetMapping("/contestants/{id}")
|
||||
@ApiOperation(value = "根据id查询参赛者", notes = "根据id获取参赛者信息")
|
||||
public Response<ContestantDTO> getContestantByID(@ApiParam(value = "参赛者id", required = true) @PathVariable("id") String id) {
|
||||
ContestantDTO dto = globalAwardService.getContestantByID(id);
|
||||
return Response.success(dto);
|
||||
}
|
||||
|
||||
@GetMapping("/checkEmail")
|
||||
public Response<String> checkEmail(@RequestParam("email") String email) {
|
||||
globalAwardService.checkEmail(email);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@GetMapping("/checkCode")
|
||||
public Response<CheckOTPVO> checkCode(@RequestParam("email") String email, @RequestParam("code") String code) {
|
||||
return Response.success(globalAwardService.checkCode(email, code));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
@@ -119,4 +122,14 @@ public class PythonController {
|
||||
return Response.success(superResolutionService.prepareForSR(superResolutionDTO));
|
||||
}
|
||||
|
||||
@CrossOrigin
|
||||
@Operation(summary = "Seg Anything 转发接口")
|
||||
@PostMapping("/segAnything")
|
||||
public Response<String> segAnything(@RequestBody Map<String, Object> payload) {
|
||||
// 将前端传来的 Map 转为 fastjson JSONObject 并转发给 python 服务
|
||||
JSONObject requestJson = (JSONObject) JSON.toJSON(payload);
|
||||
String url = pythonService.segAnything(requestJson);
|
||||
return Response.success(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class SubscriptionPlanController {
|
||||
@Operation(summary = "activeSubscriptionPlan")
|
||||
@GetMapping("/activeSubscriptionPlan")
|
||||
public Response<String> activeSubscriptionPlan() {
|
||||
subscriptionPlanService.activeSubscriptionPlan();
|
||||
subscriptionPlanService.activeSubscriptionPlan(null);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
|
||||
12
src/main/java/com/ai/da/mapper/primary/ContestantMapper.java
Normal file
12
src/main/java/com/ai/da/mapper/primary/ContestantMapper.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.ai.da.mapper.primary;
|
||||
|
||||
import com.ai.da.mapper.primary.entity.Contestant;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface ContestantMapper extends BaseMapper<Contestant> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.ai.da.mapper.primary;
|
||||
|
||||
import com.ai.da.common.config.mybatis.plus.CommonMapper;
|
||||
import com.ai.da.mapper.primary.entity.UserPreference;
|
||||
|
||||
public interface UserPreferenceMapper extends CommonMapper<UserPreference> {
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package com.ai.da.mapper.primary;
|
||||
import com.ai.da.common.config.mybatis.plus.CommonMapper;
|
||||
import com.ai.da.mapper.primary.entity.WorkspaceRelStyle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Mapper 接口
|
||||
*
|
||||
@@ -11,5 +13,11 @@ import com.ai.da.mapper.primary.entity.WorkspaceRelStyle;
|
||||
*/
|
||||
public interface WorkspaceRelStyleMapper extends CommonMapper<WorkspaceRelStyle> {
|
||||
|
||||
/**
|
||||
* 根据projectId查询workspaceRelStyles
|
||||
* @param projectId 项目ID
|
||||
* @return workspaceRelStyles列表
|
||||
*/
|
||||
List<WorkspaceRelStyle> selectByProjectId(Long projectId);
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -76,13 +77,13 @@ public class Account implements Serializable {
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createDate;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date updateDate;
|
||||
|
||||
private Integer isTrial;
|
||||
@@ -142,4 +143,26 @@ public class Account implements Serializable {
|
||||
private String givenName;
|
||||
|
||||
private Long subscriptionPlanId;
|
||||
|
||||
// 在类内部定义的枚举
|
||||
@Getter
|
||||
public enum SystemRole {
|
||||
VISITOR("游客", 0),
|
||||
YEARLY("年付用户", 1),
|
||||
MONTHLY("月付用户", 2),
|
||||
TRIAL("试用用户", 3),
|
||||
EVENT_USER("参加活动获取30天有效期和6000个积分的用户", 4),
|
||||
ENTERPRISE_ADMIN("企业管理员账号", 5),
|
||||
ENTERPRISE_SUB("企业子账号", 6),
|
||||
EDUCATION_ADMIN("学校管理员", 7),
|
||||
EDUCATION_SUB("学校子账号", 8);
|
||||
|
||||
private final String desc;
|
||||
private final int code;
|
||||
|
||||
SystemRole(String desc, int code) {
|
||||
this.desc = desc;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.ai.da.mapper.primary.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* submissions 表对应实体 — 参赛选手信息 (Contestant)
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@TableName("contestants")
|
||||
public class Contestant {
|
||||
|
||||
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
private String email;
|
||||
|
||||
@TableField("first_name")
|
||||
private String firstName;
|
||||
|
||||
@TableField("last_name")
|
||||
private String lastName;
|
||||
|
||||
private String gender;
|
||||
|
||||
private String occupation;
|
||||
|
||||
private Integer age;
|
||||
|
||||
@TableField("country_region_city")
|
||||
private String countryRegionCity;
|
||||
|
||||
@TableField("phone_number")
|
||||
private String phoneNumber;
|
||||
|
||||
@TableField("design_title")
|
||||
private String designTitle;
|
||||
|
||||
@TableField("design_description")
|
||||
private String designDescription;
|
||||
|
||||
@TableField("pdf_path")
|
||||
private String pdfPath;
|
||||
|
||||
@TableField("video_path")
|
||||
private String videoPath;
|
||||
|
||||
@TableField("created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@TableField("updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -62,4 +62,9 @@ public class DesignItemDetailPrint {
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateDate;
|
||||
|
||||
/**
|
||||
* 对象信息(JSON格式)
|
||||
*/
|
||||
private String object;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,11 @@ public class SubscriptionPlan extends BaseEntity{
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 国家或地区
|
||||
*/
|
||||
private String countryOrRegion;
|
||||
|
||||
// 在类内部定义的枚举
|
||||
@Getter
|
||||
public enum SubscriptionStatus {
|
||||
|
||||
@@ -90,6 +90,19 @@ public class TDesignPythonOutfitDetail implements Serializable {
|
||||
*/
|
||||
@Schema(description = "图层优先级")
|
||||
private Integer priority;
|
||||
|
||||
/**
|
||||
* 镜像模式
|
||||
*/
|
||||
@Schema(description = "镜像模式")
|
||||
private String transpose;
|
||||
|
||||
/**
|
||||
* 旋转角度
|
||||
*/
|
||||
@Schema(description = "旋转角度")
|
||||
private Double rotate;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.ai.da.mapper.primary.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@TableName("user_preference")
|
||||
public class UserPreference implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long accountId;
|
||||
private String path;
|
||||
private LocalDateTime dataTime;
|
||||
private String category;
|
||||
private String style;
|
||||
private Long workspaceRelStyleId;
|
||||
private Long projectId;
|
||||
private Long designItemId;
|
||||
|
||||
}
|
||||
63
src/main/java/com/ai/da/model/dto/ContestantDTO.java
Normal file
63
src/main/java/com/ai/da/model/dto/ContestantDTO.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Contestant request DTO for Global Award
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "参赛者信息", description = "全球奖项大赛参赛者信息数据传输对象")
|
||||
public class ContestantDTO {
|
||||
@ApiModelProperty(value = "邮箱地址", required = true, example = "user@example.com")
|
||||
private String email;
|
||||
|
||||
@ApiModelProperty(value = "名字", required = true, example = "John")
|
||||
private String firstName;
|
||||
|
||||
@ApiModelProperty(value = "姓氏", required = true, example = "Doe")
|
||||
private String lastName;
|
||||
|
||||
@ApiModelProperty(value = "性别", required = true, example = "Male", allowableValues = "Male,Female,Other")
|
||||
private String gender;
|
||||
|
||||
@ApiModelProperty(value = "职业", required = true, example = "Designer")
|
||||
private String occupation;
|
||||
|
||||
@ApiModelProperty(value = "年龄", required = true, example = "25")
|
||||
private Integer age;
|
||||
|
||||
@ApiModelProperty(value = "国家/地区/城市", required = true, example = "China/Shanghai/Shanghai")
|
||||
private String countryRegionCity;
|
||||
|
||||
@ApiModelProperty(value = "电话号码", required = true, example = "+86 138 0000 0000")
|
||||
private String phoneNumber;
|
||||
|
||||
@ApiModelProperty(value = "作品集链接", required = false, example = "https://portfolio.example.com")
|
||||
private String portfolioUrl;
|
||||
|
||||
@ApiModelProperty(value = "设计作品标题", required = true, example = "Modern Office Building Design")
|
||||
private String designTitle;
|
||||
|
||||
@ApiModelProperty(value = "设计作品描述", required = true, example = "A modern office building design featuring sustainable materials...")
|
||||
private String designDescription;
|
||||
|
||||
@ApiModelProperty(value = "PDF文件路径", required = false, example = "contestants/user@example.com/2024/01/design_1234567890.pdf")
|
||||
private String pdfPath;
|
||||
|
||||
@ApiModelProperty(value = "视频文件路径", required = false, example = "contestants/user@example.com/2024/01/video_1234567890.mp4")
|
||||
private String videoPath;
|
||||
|
||||
// /**
|
||||
// * 是否确认覆盖已存在记录(false 表示发现已有记录时仅返回 existingRecord,不覆盖)
|
||||
// */
|
||||
// @ApiModelProperty(value = "是否确认覆盖已存在记录", required = false, example = "false")
|
||||
// private Boolean confirm = false;
|
||||
|
||||
@NotBlank
|
||||
private String secureToken;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ public class DesignSingleIncludeLayersDTO implements Serializable {
|
||||
@Schema(description = "项目id")
|
||||
private Long projectId;
|
||||
|
||||
@NotBlank(message = "designType cannot be empty")
|
||||
@Schema(description = "default -> 新增sketch || merge")
|
||||
private String designType;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DesignSingleIncludeLayersDTO{" +
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import com.ai.da.mapper.primary.entity.Gradient;
|
||||
@@ -67,4 +68,18 @@ public class DesignSingleItemDTO implements Serializable {
|
||||
|
||||
private PartialDesignDTO partialDesign;
|
||||
|
||||
@Schema(description = "镜像模式 ")
|
||||
private int[] transpose;
|
||||
|
||||
@Schema(description = "45")
|
||||
private double rotate;
|
||||
|
||||
@Hidden
|
||||
@Schema(description = "带overall印花未分割图片")
|
||||
private String undividedLayerBase64;
|
||||
|
||||
@Hidden
|
||||
@Schema(description = "带overall/single印花未分割图片")
|
||||
private String undividedLayerWithSinglePrintBase64;
|
||||
|
||||
}
|
||||
|
||||
@@ -20,17 +20,17 @@ public class PartialDesignDTO implements Serializable {
|
||||
@Schema(description = "图片的base64格式")
|
||||
private String partialDesignBase64;
|
||||
|
||||
@Schema(description = "图层信息")
|
||||
private List<String> layers;
|
||||
/* @Schema(description = "图层信息")
|
||||
private List<String> layers;*/
|
||||
|
||||
public PartialDesignDTO(String partialDesignMinioPath) {
|
||||
this.partialDesignMinioPath = partialDesignMinioPath;
|
||||
}
|
||||
|
||||
public PartialDesignDTO(String partialDesignMinioPath, List<String> layers) {
|
||||
/* public PartialDesignDTO(String partialDesignMinioPath, List<String> layers) {
|
||||
this.partialDesignMinioPath = partialDesignMinioPath;
|
||||
this.layers = layers;
|
||||
}
|
||||
}*/
|
||||
|
||||
public PartialDesignDTO(String partialDesignMinioPath, String partialDesignPath) {
|
||||
this.partialDesignMinioPath = partialDesignMinioPath;
|
||||
|
||||
@@ -48,4 +48,7 @@ public class SubscriptionPlanDTO {
|
||||
@Schema(description = "订阅计划状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "国家或地区")
|
||||
private String CountryOrRegion;
|
||||
|
||||
}
|
||||
|
||||
@@ -12,9 +12,12 @@ public class SubscriptionPlanPageQuery extends QueryPageByTimeDTO {
|
||||
@Schema(description = "组织id")
|
||||
private Long organizationId;
|
||||
|
||||
@Schema(description = "管理id")
|
||||
@Schema(description = "管理员id")
|
||||
private Long adminAccId;
|
||||
|
||||
@Schema(description = "状态 PENDING||ACTIVE||EXPIRED")
|
||||
private List<String> status;
|
||||
|
||||
@Schema(description = "国家或地区")
|
||||
private String countryOrRegion;
|
||||
}
|
||||
|
||||
@@ -32,4 +32,7 @@ public class UpdateSubscriptionPlanDTO {
|
||||
@Schema(description = "订阅重命名")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "国家或地区")
|
||||
private String countryOrRegion;
|
||||
|
||||
}
|
||||
|
||||
33
src/main/java/com/ai/da/model/dto/UploadChunkResponse.java
Normal file
33
src/main/java/com/ai/da/model/dto/UploadChunkResponse.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 分片上传响应DTO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@ApiModel(value = "分片上传响应", description = "单个文件分片上传成功的响应数据")
|
||||
public class UploadChunkResponse {
|
||||
|
||||
/**
|
||||
* 分片索引
|
||||
*/
|
||||
@ApiModelProperty(value = "分片索引(从0开始)", required = true, example = "0")
|
||||
private Integer chunkIndex;
|
||||
|
||||
/**
|
||||
* 是否上传成功
|
||||
*/
|
||||
@ApiModelProperty(value = "是否上传成功", required = true, example = "true")
|
||||
private Boolean uploaded;
|
||||
|
||||
/**
|
||||
* 分片大小(字节)
|
||||
*/
|
||||
@ApiModelProperty(value = "分片大小(字节)", required = true, example = "1048576")
|
||||
private Long size;
|
||||
}
|
||||
53
src/main/java/com/ai/da/model/dto/UploadCompleteRequest.java
Normal file
53
src/main/java/com/ai/da/model/dto/UploadCompleteRequest.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
/**
|
||||
* 完成上传请求DTO
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "完成上传请求", description = "文件上传完成时使用的请求参数")
|
||||
public class UploadCompleteRequest {
|
||||
|
||||
/**
|
||||
* 上传任务ID
|
||||
*/
|
||||
@NotBlank(message = "上传任务ID不能为空")
|
||||
@ApiModelProperty(value = "上传任务唯一标识", required = true, example = "550e8400-e29b-41d4-a716-446655440000")
|
||||
private String uploadId;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
@NotBlank(message = "文件名不能为空")
|
||||
@ApiModelProperty(value = "原始文件名", required = true, example = "design.pdf")
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 文件总大小(字节)
|
||||
*/
|
||||
@NotNull(message = "文件大小不能为空")
|
||||
@Positive(message = "文件大小必须大于0")
|
||||
@ApiModelProperty(value = "文件总大小(字节)", required = true, example = "10485760")
|
||||
private Long totalSize;
|
||||
|
||||
/**
|
||||
* 用户邮箱
|
||||
*/
|
||||
@NotBlank(message = "用户邮箱不能为空")
|
||||
@ApiModelProperty(value = "用户邮箱", required = true, example = "user@example.com")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 安全令牌(邮箱验证令牌)
|
||||
*/
|
||||
@NotBlank(message = "安全令牌不能为空")
|
||||
@ApiModelProperty(value = "安全令牌", required = true, example = "abc123def456")
|
||||
private String secureToken;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 完成上传响应DTO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@ApiModel(value = "完成上传响应", description = "文件上传完成并合并成功的响应数据")
|
||||
public class UploadCompleteResponse {
|
||||
|
||||
/**
|
||||
* 文件在MinIO中的路径
|
||||
*/
|
||||
@ApiModelProperty(value = "文件在MinIO中的存储路径", required = true, example = "contestants/user@example.com/2024/01/design_1234567890.pdf")
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
* 文件的完整URL
|
||||
*/
|
||||
@ApiModelProperty(value = "文件的完整访问URL", required = true, example = "https://minio.example.com/contestants/user@example.com/2024/01/design_1234567890.pdf")
|
||||
private String fileUrl;
|
||||
|
||||
/**
|
||||
* 文件大小(字节)
|
||||
*/
|
||||
@ApiModelProperty(value = "文件大小(字节)", required = true, example = "10485760")
|
||||
private Long fileSize;
|
||||
}
|
||||
53
src/main/java/com/ai/da/model/dto/UploadInitRequest.java
Normal file
53
src/main/java/com/ai/da/model/dto/UploadInitRequest.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
/**
|
||||
* 初始化上传请求DTO
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "初始化上传请求", description = "文件上传初始化时使用的请求参数")
|
||||
public class UploadInitRequest {
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
@NotBlank(message = "文件名不能为空")
|
||||
@ApiModelProperty(value = "文件名", required = true, example = "design.pdf")
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 文件大小(字节)
|
||||
*/
|
||||
@NotNull(message = "文件大小不能为空")
|
||||
@Positive(message = "文件大小必须大于0")
|
||||
@ApiModelProperty(value = "文件大小(字节)", required = true, example = "10485760")
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 文件类型(MIME类型)
|
||||
*/
|
||||
@NotBlank(message = "文件类型不能为空")
|
||||
@ApiModelProperty(value = "文件类型(MIME类型)", required = true, example = "application/pdf")
|
||||
private String fileType;
|
||||
|
||||
|
||||
/**
|
||||
* 用户邮箱
|
||||
*/
|
||||
@ApiModelProperty(value = "用户邮箱", required = true, example = "user@example.com")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 安全令牌(邮箱验证令牌)
|
||||
*/
|
||||
@NotBlank(message = "安全令牌不能为空")
|
||||
@ApiModelProperty(value = "安全令牌", required = true, example = "abc123def456")
|
||||
private String secureToken;
|
||||
}
|
||||
41
src/main/java/com/ai/da/model/dto/UploadInitResponse.java
Normal file
41
src/main/java/com/ai/da/model/dto/UploadInitResponse.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 初始化上传响应DTO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@ApiModel(value = "初始化上传响应", description = "文件上传初始化成功的响应数据")
|
||||
public class UploadInitResponse {
|
||||
|
||||
/**
|
||||
* 上传任务ID
|
||||
*/
|
||||
@ApiModelProperty(value = "上传任务唯一标识", required = true, example = "550e8400-e29b-41d4-a716-446655440000")
|
||||
private String uploadId;
|
||||
|
||||
/**
|
||||
* 分片大小(字节)
|
||||
*/
|
||||
@ApiModelProperty(value = "每个分片的大小(字节)", required = true, example = "1048576")
|
||||
private Integer chunkSize;
|
||||
|
||||
/**
|
||||
* 总分片数
|
||||
*/
|
||||
@ApiModelProperty(value = "文件被分成多少个分片", required = true, example = "10")
|
||||
private Integer totalChunks;
|
||||
|
||||
/**
|
||||
* 任务过期时间
|
||||
*/
|
||||
@ApiModelProperty(value = "上传任务过期时间", required = true, example = "2024-01-20T10:30:00")
|
||||
private LocalDateTime expiresAt;
|
||||
}
|
||||
59
src/main/java/com/ai/da/model/dto/UploadStatusResponse.java
Normal file
59
src/main/java/com/ai/da/model/dto/UploadStatusResponse.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 上传状态响应DTO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@ApiModel(value = "上传状态响应", description = "查询上传任务当前状态的响应数据")
|
||||
public class UploadStatusResponse {
|
||||
|
||||
/**
|
||||
* 上传任务ID
|
||||
*/
|
||||
@ApiModelProperty(value = "上传任务唯一标识", required = true, example = "550e8400-e29b-41d4-a716-446655440000")
|
||||
private String uploadId;
|
||||
|
||||
/**
|
||||
* 上传状态
|
||||
*/
|
||||
@ApiModelProperty(value = "上传任务状态", required = true, example = "uploading", allowableValues = "initiated,uploading,completed,failed,expired")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 上传进度百分比 (0-100)
|
||||
*/
|
||||
@ApiModelProperty(value = "上传进度百分比(0-100)", required = true, example = "60.0")
|
||||
private Double progress;
|
||||
|
||||
/**
|
||||
* 已上传分片索引集合
|
||||
*/
|
||||
@ApiModelProperty(value = "已上传分片的索引集合", required = true, example = "[0,1,2,3,4]")
|
||||
private Set<Integer> uploadedChunks;
|
||||
|
||||
/**
|
||||
* 总分片数
|
||||
*/
|
||||
@ApiModelProperty(value = "文件被分成多少个分片", required = true, example = "10")
|
||||
private Integer totalChunks;
|
||||
|
||||
/**
|
||||
* 文件总大小(字节)
|
||||
*/
|
||||
@ApiModelProperty(value = "文件总大小(字节)", required = true, example = "10485760")
|
||||
private Long totalSize;
|
||||
|
||||
/**
|
||||
* 已上传大小(字节)
|
||||
*/
|
||||
@ApiModelProperty(value = "已上传的数据大小(字节)", required = true, example = "6291456")
|
||||
private Long uploadedSize;
|
||||
}
|
||||
16
src/main/java/com/ai/da/model/vo/CheckOTPVO.java
Normal file
16
src/main/java/com/ai/da/model/vo/CheckOTPVO.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.ai.da.model.vo;
|
||||
|
||||
import com.ai.da.model.dto.ContestantDTO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CheckOTPVO {
|
||||
|
||||
private String secureToken;
|
||||
|
||||
private ContestantDTO contestantDTO;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
package com.ai.da.model.vo;
|
||||
|
||||
package com.ai.da.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import com.ai.da.mapper.primary.entity.Gradient;
|
||||
@@ -62,11 +62,11 @@ public class DesignItemClothesDetailVO {
|
||||
@Schema(description = "渐变色信息")
|
||||
private Gradient gradient;
|
||||
|
||||
@Schema(description = "未分割的图层")
|
||||
/* @Schema(description = "未分割的图层")
|
||||
private String undividedLayer;
|
||||
|
||||
@Schema(description = "添加single印花的未分割的图层")
|
||||
private String undividedLayerWithSinglePrint;
|
||||
private String undividedLayerWithSinglePrint;*/
|
||||
|
||||
@Schema(description = "局部design")
|
||||
private PartialDesignDTO partialDesign;
|
||||
|
||||
@@ -59,4 +59,16 @@ public class DesignPythonOutfitVO {
|
||||
* 图层优先级 从10开始,优先级数字越大越靠近上层
|
||||
*/
|
||||
private Integer priority;
|
||||
|
||||
/**
|
||||
* 镜像模式
|
||||
*/
|
||||
@Schema(description = "镜像模式")
|
||||
private int[] transpose;
|
||||
|
||||
/**
|
||||
* 旋转角度
|
||||
*/
|
||||
@Schema(description = "旋转角度")
|
||||
private Double rotate;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ public class DesignSinglePrint implements Serializable {
|
||||
@Schema(description = "印花优先级")
|
||||
private Integer priority;
|
||||
|
||||
private String object;
|
||||
|
||||
public DesignSinglePrint() {
|
||||
}
|
||||
|
||||
|
||||
@@ -45,4 +45,7 @@ public class SubscriptionPlanVO {
|
||||
@Schema(description = "命名")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "国家或地区")
|
||||
private String countryOrRegion;
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ public class DesignPythonBasic {
|
||||
|
||||
private String single_overall;
|
||||
|
||||
private String preview_submit;
|
||||
// private String preview_submit;
|
||||
|
||||
private String switch_category;
|
||||
/**
|
||||
@@ -40,4 +40,7 @@ public class DesignPythonBasic {
|
||||
|
||||
private Boolean layer_order = Boolean.FALSE;
|
||||
|
||||
// default | merge
|
||||
private String design_type = "default";
|
||||
|
||||
}
|
||||
|
||||
@@ -87,6 +87,22 @@ public class DesignPythonItem {
|
||||
*/
|
||||
private String seg_mask_url;
|
||||
|
||||
/**
|
||||
* 镜像模式
|
||||
*/
|
||||
private int[] transpose;
|
||||
|
||||
/**
|
||||
* 旋转角度
|
||||
*/
|
||||
private double rotate;
|
||||
|
||||
/**
|
||||
* 前端处理了print之后的结果图,python对该图进行分割
|
||||
* designType为merge时,该字段必须有值,否则会导致python端没有数据返回
|
||||
*/
|
||||
private String merge_image_path;
|
||||
|
||||
public static List<String> OUTWEAR_DRESS_BLOUSE = Arrays.asList(CollectionLevel2TypeEnum.OUTWEAR.getRealName(),
|
||||
CollectionLevel2TypeEnum.DRESS.getRealName(), CollectionLevel2TypeEnum.BLOUSE.getRealName());
|
||||
|
||||
@@ -143,7 +159,8 @@ public class DesignPythonItem {
|
||||
|
||||
public DesignPythonItem(String type, String path, String color, PrintToPython print, Long businessId,
|
||||
Long image_id, List<Long> offset, Float[] resize_scale, Integer priority, String gradient,
|
||||
String gradientString, String seg_mask_url) {
|
||||
String gradientString, String seg_mask_url, int[] transpose, double rotate,
|
||||
String merge_image_path) {
|
||||
this.type = type;
|
||||
this.path = path;
|
||||
this.color = color;
|
||||
@@ -157,6 +174,9 @@ public class DesignPythonItem {
|
||||
this.gradient = gradient;
|
||||
this.gradientString = gradientString;
|
||||
this.seg_mask_url = seg_mask_url;
|
||||
this.transpose = transpose;
|
||||
this.rotate = rotate;
|
||||
this.merge_image_path = merge_image_path;
|
||||
}
|
||||
|
||||
public DesignPythonItem(String type, String path, String color, PrintToPython print, String icon, Long businessId, Long image_id) {
|
||||
|
||||
@@ -246,4 +246,6 @@ public interface AccountService extends IService<Account> {
|
||||
void setEduAdminToExpire(Account adminAccount);
|
||||
|
||||
String getOrganizationTypeByRole(Integer roleNum);
|
||||
|
||||
void validateUserValidaExpire(Account account);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public interface ConvenientInquiryService extends IService<Questionnaire> {
|
||||
|
||||
IPage<Account> getUserInfo(QueryUserConditionsVO queryUserConditionsVO);
|
||||
|
||||
List<Map<String, Object>> getAllUserIdList();
|
||||
IPage<Map<String, Object>> getAllUserIdList(Integer pageNum, Integer pageSize, String email);
|
||||
|
||||
PageBaseResponse<PaymentInfoVO> queryTransactionRecords(QueryPaymentInfoDTO queryPaymentInfoDTO);
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public interface DesignItemService extends IService<DesignItem> {
|
||||
|
||||
DesignSingleVO designSingleIncludeLayers(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO);
|
||||
|
||||
Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers);
|
||||
Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers, DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO);
|
||||
|
||||
Map<String, String> setTypeAndUndividedLayer(JSONArray layers);
|
||||
|
||||
|
||||
25
src/main/java/com/ai/da/service/GlobalAwardService.java
Normal file
25
src/main/java/com/ai/da/service/GlobalAwardService.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.ai.da.service;
|
||||
|
||||
import com.ai.da.model.dto.ContestantDTO;
|
||||
import com.ai.da.model.vo.CheckOTPVO;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface GlobalAwardService {
|
||||
String uploadPdf(MultipartFile file, String email) throws Exception;
|
||||
|
||||
String uploadVideo(MultipartFile file, String email) throws Exception;
|
||||
|
||||
Map<String, Object> saveContestant(ContestantDTO request);
|
||||
|
||||
com.ai.da.model.dto.ContestantDTO getContestantByID(String email);
|
||||
|
||||
void checkEmail(String email);
|
||||
|
||||
CheckOTPVO checkCode(String email, String otp);
|
||||
|
||||
void checkSecurityToken(String email, String securityToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ public interface SubscriptionPlanService extends IService<SubscriptionPlan> {
|
||||
|
||||
void switchSubAccSubscriptionPlan(Long subscriptionPlanId, Long subAccId);
|
||||
|
||||
void activeSubscriptionPlan();
|
||||
void activeSubscriptionPlan(Long planId);
|
||||
|
||||
void expireSubscription();
|
||||
}
|
||||
|
||||
@@ -39,9 +39,10 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -83,6 +84,9 @@ import java.util.stream.Collectors;
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Resource
|
||||
private AccountMapper accountMapper;
|
||||
|
||||
@@ -279,7 +283,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
// 定义常量(临时)
|
||||
private static final Integer SYSTEM_USER_TYPE_EDU_ADMIN = 7;
|
||||
|
||||
private void validateUserValidaExpire(Account account) {
|
||||
public void validateUserValidaExpire(Account account) {
|
||||
Long currentTime = new Date().getTime();
|
||||
if (account.getSystemUser().equals(0)) {
|
||||
return;
|
||||
@@ -297,7 +301,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
if (isEduAdmin) {
|
||||
setEduAdminToExpire(account);
|
||||
} else {
|
||||
toVisitor(account);
|
||||
// 这里调用代理的 toVisitor 方法
|
||||
AccountService proxy = applicationContext.getBean(AccountService.class);
|
||||
proxy.toVisitor(account);
|
||||
// return;
|
||||
throw new BusinessException("account.expired", ResultEnum.PROMPT.getCode());
|
||||
}
|
||||
@@ -1949,6 +1955,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
return baseMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void toVisitor(Account account) {
|
||||
accountMapper.toVisitor(account.getId());
|
||||
}
|
||||
@@ -2507,7 +2514,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
}
|
||||
|
||||
// 判断当前账号的有效期是否与管理员同步
|
||||
if (subAccount.getValidEndTime() < adminAcc.getValidEndTime()){
|
||||
if (Objects.isNull(subAccount.getValidEndTime()) || subAccount.getValidEndTime() < adminAcc.getValidEndTime()){
|
||||
subAccount.setValidEndTime(adminAcc.getValidEndTime());
|
||||
}
|
||||
|
||||
|
||||
@@ -798,27 +798,46 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|
||||
return accountMapper.selectPage(new Page<>(queryUserConditionsVO.getPage(), queryUserConditionsVO.getSize()), queryWrapper);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getAllUserIdList() {
|
||||
public IPage<Map<String, Object>> getAllUserIdList(Integer pageNum, Integer pageSize, String email) {
|
||||
Long accountId = UserContext.getUserHolder().getId();
|
||||
Account account = accountMapper.selectById(accountId);
|
||||
// 允许查看数据的用户id
|
||||
if (Objects.nonNull(account.getSystemUser())
|
||||
&& (account.getSystemUser().equals(5)
|
||||
|| account.getSystemUser().equals(7)
|
||||
|| ADMIN_IDS.contains(account.getId())
|
||||
|| ADMIN_IDS_READ_ONLY.contains(account.getId())
|
||||
)) {
|
||||
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.select("id as value, user_name as label");
|
||||
if ((account.getSystemUser().equals(7) || account.getSystemUser().equals(5))
|
||||
&& !StringUtil.isNullOrEmpty(account.getOrganizationName())) {
|
||||
queryWrapper.lambda().eq(Account::getOrganizationName, account.getOrganizationName());
|
||||
}
|
||||
return accountMapper.selectMaps(queryWrapper);
|
||||
} else {
|
||||
|
||||
// 权限校验
|
||||
if (Objects.isNull(account.getSystemUser())
|
||||
|| (!account.getSystemUser().equals(5)
|
||||
&& !account.getSystemUser().equals(7)
|
||||
&& !ADMIN_IDS.contains(account.getId())
|
||||
&& !ADMIN_IDS_READ_ONLY.contains(account.getId()))) {
|
||||
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode());
|
||||
}
|
||||
// return maps.stream().map(map -> (Long)map.get("id")).collect(Collectors.toList());
|
||||
|
||||
// 创建分页对象
|
||||
Page<Account> page = new Page<>(pageNum, pageSize);
|
||||
|
||||
// 构建查询条件
|
||||
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.select("id", "user_name", "user_email");
|
||||
|
||||
if ((account.getSystemUser().equals(7) || account.getSystemUser().equals(5))
|
||||
&& !StringUtil.isNullOrEmpty(account.getOrganizationName())) {
|
||||
queryWrapper.lambda().eq(Account::getOrganizationName, account.getOrganizationName());
|
||||
}
|
||||
|
||||
if (!StringUtil.isNullOrEmpty(email)) {
|
||||
queryWrapper.lambda().like(Account::getUserEmail, email);
|
||||
}
|
||||
|
||||
// 执行分页查询
|
||||
IPage<Account> accountPage = accountMapper.selectPage(page, queryWrapper);
|
||||
|
||||
// 转换为 IPage<Map> 并重命名字段
|
||||
return accountPage.convert(acc -> {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("value", acc.getId());
|
||||
map.put("label", acc.getUserName());
|
||||
map.put("email", acc.getUserEmail());
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -363,9 +363,9 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
public List<TDesignPythonOutfitDetail> saveDesignSingleItemDetailAndLayers(DesignPythonObjects pythonObjects
|
||||
, Long designId, Long designItemId, Long userId
|
||||
, JSONObject outfit, String timeZone, List<DesignSingleItemDTO> designSingleItemDTOList
|
||||
, Map<String, List<String>> priorityAndUndividedLayer
|
||||
/*, Map<String, List<String>> priorityAndUndividedLayer*/
|
||||
, boolean changeModelFlag
|
||||
, Long modelId, String modelType, boolean isSingleCollectionFlag) {
|
||||
, Long modelId, String modelType, boolean isSingleCollectionFlag, String designType) {
|
||||
|
||||
DesignItem designItem = new DesignItem();
|
||||
// String url = pythonObjects.getObjects().get(0).getBasic().getSave_name();
|
||||
@@ -424,11 +424,15 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
designItemDetail.setColor(detail.getColor());
|
||||
designItemDetail.setIconPath(detail.getIcon());
|
||||
// designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getType().toLowerCase()));
|
||||
/*// 取消存储UndividedLayer和UndividedLayerWithSinglePrint字段
|
||||
if (!detail.getType().equals("Body")) {
|
||||
designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(0));
|
||||
designItemDetail.setUndividedLayerWithSinglePrint(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(1));
|
||||
|
||||
}
|
||||
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(0))) {
|
||||
designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getPriority().toString()).getFirst());
|
||||
}
|
||||
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(1))) {
|
||||
designItemDetail.setUndividedLayerWithSinglePrint(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(1));
|
||||
}
|
||||
}*/
|
||||
// 印花存储在design_item_detail_print表中 这里还要存吗?
|
||||
// DesignPythonItemPrint printObject = detail.getPrintToPython();
|
||||
// designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath());
|
||||
@@ -436,7 +440,18 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
// designItemDetail.setPrintJson(JSON.toJSONString(printObject));
|
||||
|
||||
designItemDetail.setPartialDesign(Objects.isNull(detail.getPrint()) ? null : detail.getPrint().getPartial());
|
||||
designItemDetail.setGradientString(detail.getGradientString());
|
||||
|
||||
designItemDetails.add(designItemDetail);
|
||||
|
||||
// 处理gradientString为null的情况,强制更新为null
|
||||
if (Objects.isNull(detail.getGradientString()) && Objects.nonNull(designItemDetail.getId())) {
|
||||
UpdateWrapper<DesignItemDetail> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("id", designItemDetail.getId());
|
||||
updateWrapper.set("gradient_string", null);
|
||||
updateWrapper.set("update_date", DateUtil.getByTimeZone(timeZone));
|
||||
designItemDetailService.update(null, updateWrapper);
|
||||
}
|
||||
});
|
||||
|
||||
// 逻辑删除未复用的旧记录
|
||||
@@ -481,6 +496,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
priorityOffset = designSingleItemDTOList.stream()
|
||||
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset));
|
||||
}
|
||||
|
||||
List<TDesignPythonOutfitDetail> list = new ArrayList<>();
|
||||
for (int i = 0; i < layers.size(); i++) {
|
||||
JSONObject jsonObject = layers.getJSONObject(i);
|
||||
@@ -509,6 +525,8 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(priority))));
|
||||
}
|
||||
designPythonOutfitDetail.setPriority(priority);
|
||||
designPythonOutfitDetail.setTranspose(jsonObject.getString("transpose"));
|
||||
designPythonOutfitDetail.setRotate(jsonObject.getDouble("rotate"));
|
||||
|
||||
list.add(designPythonOutfitDetail);
|
||||
}
|
||||
@@ -683,6 +701,9 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public DesignSingleVO designSingleIncludeLayers(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {
|
||||
if (!designSingleIncludeLayersDTO.getDesignType().equals("merge") && !designSingleIncludeLayersDTO.getDesignType().equals("default")) {
|
||||
throw new BusinessException("The value of DesignType can only be 'default' or 'merge' ");
|
||||
}
|
||||
// 记录入参 base64数据太长,所以这里去掉
|
||||
DesignSingleIncludeLayersDTO clone = SerializationUtils.clone(designSingleIncludeLayersDTO);
|
||||
clone.getDesignSingleItemDTOList().forEach(i -> {
|
||||
@@ -706,6 +727,17 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
log.info("set partialDesignBase64为空,便于日志打印");
|
||||
i.getPartialDesign().setPartialDesignBase64(null);
|
||||
}
|
||||
// undividedLayerBase64 undividedLayerWithSinglePrintBase64 置空
|
||||
/*// 前端合成的未分割的图
|
||||
if (!StringUtil.isNullOrEmpty(i.getUndividedLayerBase64())) {
|
||||
log.info("set UndividedLayerBase64为空,便于日志打印");
|
||||
i.setUndividedLayerBase64(null);
|
||||
}
|
||||
// 前端合成的未分割的图
|
||||
if (!StringUtil.isNullOrEmpty(i.getUndividedLayerWithSinglePrintBase64())) {
|
||||
log.info("set UndividedLayerWithSinglePrintBase64为空,便于日志打印");
|
||||
i.setUndividedLayerWithSinglePrintBase64(null);
|
||||
}*/
|
||||
});
|
||||
|
||||
log.info("designSingle request入参 ==> " + JSONObject.toJSONString(clone));
|
||||
@@ -800,7 +832,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
maskBase64ToPath(designSingleIncludeLayersDTO, setNull);
|
||||
// maskBase64ToPath(designSingleIncludeLayersDTO, Boolean.TRUE);
|
||||
|
||||
partialDesignBase64ToImage(designSingleIncludeLayersDTO, userId, designSingleIncludeLayersDTO.getIsPreview());
|
||||
partialDesignBase64ToImage(designSingleIncludeLayersDTO, userId, designSingleIncludeLayersDTO.getIsPreview(), designSingleIncludeLayersDTO.getDesignType());
|
||||
|
||||
// 组装入参
|
||||
DesignPythonObjects objects = pythonService.covertDesignSingleParam(
|
||||
@@ -818,13 +850,13 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
JSONObject outfit = data.getJSONObject("0");
|
||||
|
||||
JSONArray layers = outfit.getJSONArray("layers");
|
||||
Map<String, List<String>> priorityAndUndividedLayer = setPriorityAndUndividedLayer(layers);
|
||||
// Map<String, List<String>> priorityAndUndividedLayer = setPriorityAndUndividedLayer(layers, designSingleIncludeLayersDTO);
|
||||
if (!designSingleIncludeLayersDTO.getIsPreview()) {
|
||||
// 更新及保存图层信息
|
||||
tDesignPythonOutfitDetails = saveDesignSingleItemDetailAndLayers(objects, design.getId(), designSingleIncludeLayersDTO.getDesignItemId()
|
||||
, userId, outfit, designSingleIncludeLayersDTO.getTimeZone()
|
||||
, designSingleIncludeLayersDTO.getDesignSingleItemDTOList()
|
||||
, priorityAndUndividedLayer, changeModelFlag, modelId, modelType, isSingleCollectionFlag);
|
||||
/*, priorityAndUndividedLayer*/, changeModelFlag, modelId, modelType, isSingleCollectionFlag, designSingleIncludeLayersDTO.getDesignType());
|
||||
|
||||
saveCollectionElement(designSingleIncludeLayersDTO);
|
||||
} else {
|
||||
@@ -858,8 +890,8 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
outfit.getString("synthesis_url"),
|
||||
designSingleIncludeLayersDTO.getDesignSingleItemDTOList(),
|
||||
detailsVO,
|
||||
design.getSingleOverall(),
|
||||
priorityAndUndividedLayer);
|
||||
design.getSingleOverall()/*,
|
||||
priorityAndUndividedLayer*/);
|
||||
}
|
||||
|
||||
// 方法1:仅查询(无事务)
|
||||
@@ -919,12 +951,12 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
item.setMaskUrl(path);
|
||||
}
|
||||
}
|
||||
log.info("服装{} 的maskUrl为null", item.getType());
|
||||
// log.info("服装{} 的maskUrl为null", item.getType());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void partialDesignBase64ToImage(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO, Long accountId, boolean preview) {
|
||||
private void partialDesignBase64ToImage(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO, Long accountId, boolean preview, String designType) {
|
||||
designSingleIncludeLayersDTO.getDesignSingleItemDTOList().forEach(item -> {
|
||||
PartialDesignDTO partialDesignDTO = item.getPartialDesign();
|
||||
if (!Objects.isNull(item.getPartialDesign())
|
||||
@@ -946,21 +978,90 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
item.getPartialDesign().setPartialDesignMinioPath(newPath);
|
||||
} else if (Objects.isNull(item.getPartialDesign())
|
||||
|| StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignMinioPath())) {
|
||||
item.setPartialDesign(new PartialDesignDTO(null));
|
||||
if (designType.equals("merge")) {
|
||||
// 先去数据库进行查找,如果数据库中也是空,则提示需要提供,否则无法生成
|
||||
DesignItemDetail designItemDetail = designItemDetailService.getById(item.getId());
|
||||
if (Objects.isNull(designItemDetail)){
|
||||
log.error("未知designItemDetailId: {}", item.getId());
|
||||
throw new BusinessException("designItemDetails.not.found");
|
||||
} else if (StringUtil.isNullOrEmpty(designItemDetail.getPartialDesign())) {
|
||||
item.setPartialDesign(new PartialDesignDTO(designItemDetail.getUndividedLayer()));
|
||||
/*log.error("merge模式下,必须提供partialDesign");
|
||||
throw new BusinessException("required.partialDesign");*/
|
||||
} else {
|
||||
item.setPartialDesign(new PartialDesignDTO(designItemDetail.getPartialDesign()));
|
||||
}
|
||||
} else {
|
||||
item.setPartialDesign(new PartialDesignDTO(null));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void undividedLayerBase64ToImage(List<DesignSingleItemDTO> designSingleItemDTOS) {
|
||||
designSingleItemDTOS.forEach(item -> {
|
||||
if (!StringUtil.isNullOrEmpty(item.getUndividedLayerBase64())) {
|
||||
if (item.getUndividedLayerBase64().startsWith("data:image") && !item.getUndividedLayerBase64().startsWith("https://")) {
|
||||
// 将原图地址作为修改后的图片地址,放在不同的桶
|
||||
String filename = "image/image_" + UUID.randomUUID();
|
||||
String path = minioUtil.base64UploadToPath(item.getUndividedLayerBase64(), clothingBucket, filename);
|
||||
log.info("undividedLayer, 新的path为{}", path);
|
||||
if (StringUtil.isNullOrEmpty(path)) {
|
||||
log.error("undividedLayer图片base64上传失败");
|
||||
throw new BusinessException("file.upload.fail");
|
||||
}
|
||||
item.setUndividedLayerBase64(path);
|
||||
} else {
|
||||
item.setUndividedLayerBase64(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (!StringUtil.isNullOrEmpty(item.getUndividedLayerWithSinglePrintBase64())) {
|
||||
if (item.getUndividedLayerWithSinglePrintBase64().startsWith("data:image") && !item.getUndividedLayerWithSinglePrintBase64().startsWith("https://")) {
|
||||
// 将原图地址作为修改后的图片地址,放在不同的桶
|
||||
String filename = "image/image_" + UUID.randomUUID();
|
||||
String path = minioUtil.base64UploadToPath(item.getUndividedLayerWithSinglePrintBase64(), clothingBucket, filename);
|
||||
log.info("getUndividedLayerWithSinglePrint, 新的path为{}", path);
|
||||
if (StringUtil.isNullOrEmpty(path)) {
|
||||
log.error("getUndividedLayerWithSinglePrintBase64图片base64上传失败");
|
||||
throw new BusinessException("file.upload.fail");
|
||||
}
|
||||
item.setUndividedLayerWithSinglePrintBase64(path);
|
||||
} else {
|
||||
item.setUndividedLayerWithSinglePrintBase64(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers) {
|
||||
HashMap<String, List<String>> priorityAndLayer = new HashMap<>();
|
||||
for (int i = 0; i < layers.size(); i++) {
|
||||
JSONObject jsonObject = layers.getJSONObject(i);
|
||||
String priority = jsonObject.getString("priority");
|
||||
String category = jsonObject.getString("image_category").split("_")[0];
|
||||
if (!category.equals("body") && !priorityAndLayer.containsKey(priority))
|
||||
priorityAndLayer.put(priority, Arrays.asList(jsonObject.getString("pattern_overall_image_url"), jsonObject.getString("pattern_print_image_url")));
|
||||
public Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers, DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {
|
||||
String designType = "default";
|
||||
if (Objects.nonNull(designSingleIncludeLayersDTO)) {
|
||||
designType = designSingleIncludeLayersDTO.getDesignType();
|
||||
}
|
||||
HashMap<String, List<String>> priorityAndLayer = new HashMap<>();
|
||||
if (designType.equals("default")) {
|
||||
for (int i = 0; i < layers.size(); i++) {
|
||||
JSONObject jsonObject = layers.getJSONObject(i);
|
||||
String priority = jsonObject.getString("priority");
|
||||
String category = jsonObject.getString("image_category").split("_")[0];
|
||||
if (!category.equals("body") && !priorityAndLayer.containsKey(priority)) {
|
||||
// pattern_overall_image_url | pattern_print_image_url 这俩字段来源有俩,merge模式下,来自前端,default模式下,来自python
|
||||
priorityAndLayer.put(priority, Arrays.asList(jsonObject.getString("pattern_overall_image_url"), jsonObject.getString("pattern_print_image_url")));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (designSingleIncludeLayersDTO.getIsPreview()) {
|
||||
// 如果是预览,则不处理、不存储前端传过来的数据
|
||||
return priorityAndLayer;
|
||||
}
|
||||
undividedLayerBase64ToImage(designSingleIncludeLayersDTO.getDesignSingleItemDTOList());
|
||||
for (DesignSingleItemDTO designSingleItemDTO : designSingleIncludeLayersDTO.getDesignSingleItemDTOList()) {
|
||||
priorityAndLayer.put(designSingleItemDTO.getPriority().toString(), Arrays.asList(designSingleItemDTO.getUndividedLayerBase64(), designSingleItemDTO.getUndividedLayerWithSinglePrintBase64()));
|
||||
}
|
||||
}
|
||||
|
||||
return priorityAndLayer;
|
||||
}
|
||||
|
||||
@@ -1075,8 +1176,8 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
String currentFullBodyView,
|
||||
List<DesignSingleItemDTO> designSingleItemDTOList,
|
||||
List<DesignPythonOutfitVO> layersObject,
|
||||
String singleOrOverall,
|
||||
Map<String, List<String>> priorityAndUndividedLayer) {
|
||||
String singleOrOverall/*,
|
||||
Map<String, List<String>> priorityAndUndividedLayer*/) {
|
||||
|
||||
DesignSingleVO designSingleVO = new DesignSingleVO();
|
||||
ArrayList<DesignItemClothesDetailVO> clothes = new ArrayList<>();
|
||||
@@ -1116,10 +1217,15 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
String preSignedUrl = StringUtil.isNullOrEmpty(partialDesignMinioPath) ? null : minioUtil.getPreSignedUrl(partialDesignMinioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true);
|
||||
designItemClothesDetailVO.setPartialDesign(new PartialDesignDTO(partialDesignMinioPath, preSignedUrl));
|
||||
|
||||
/*// 取消存储/返回UndividedLayer和UndividedLayerWithSinglePrint字段
|
||||
if (priorityAndUndividedLayer.containsKey(singleItem.getPriority().toString())) {
|
||||
designItemClothesDetailVO.setUndividedLayer(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(0), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
|
||||
designItemClothesDetailVO.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(1), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
|
||||
}
|
||||
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(0))) {
|
||||
designItemClothesDetailVO.setUndividedLayer(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).getFirst(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
|
||||
}
|
||||
if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(1))) {
|
||||
designItemClothesDetailVO.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(1), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
|
||||
}
|
||||
}*/
|
||||
body.setLayersObject(layersObject.stream().filter(layers -> layers.getImageCategory().equals("body")).collect(Collectors.toList()));
|
||||
|
||||
clothes.add(designItemClothesDetailVO);
|
||||
@@ -1201,6 +1307,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
designItemDetailPrint.setPosition(print.getLocation().toString());
|
||||
designItemDetailPrint.setAngle(print.getAngle());
|
||||
designItemDetailPrint.setPriority(priority);
|
||||
designItemDetailPrint.setObject(print.getObject());
|
||||
|
||||
designItemDetailPrints.add(designItemDetailPrint);
|
||||
});
|
||||
@@ -1294,6 +1401,9 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
}
|
||||
}
|
||||
|
||||
if (Objects.isNull(designSingleItem.getPrintObject()) || Objects.isNull(designSingleItem.getPrintObject().getPrints())) {
|
||||
return;
|
||||
}
|
||||
// 添加print到library
|
||||
designSingleItem.getPrintObject().getPrints().forEach(print -> {
|
||||
if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Collection")) {
|
||||
|
||||
@@ -101,8 +101,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
private final UserLikeGroupService userLikeGroupService;
|
||||
private final UserLikeService userLikeService;
|
||||
private final UserBehaviorMapper userBehaviorMapper;
|
||||
private final UserPreferenceLogMapper userPreferenceLogMapper;
|
||||
private final UserPreferenceMapper userPreferenceMapper;
|
||||
private final WorkspaceService workspaceService;
|
||||
private final WorkspaceRelStyleMapper workspaceRelStyleMapper;
|
||||
|
||||
@Value("${minio.endpoint}")
|
||||
private String endpoint;
|
||||
@@ -714,7 +715,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
(existing, replacement) -> replacement));
|
||||
Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
||||
log.info("all typeLayers Map:{}", typeAndUndividedLayer);
|
||||
Map<String, List<String>> priorityAndUndividedLayer = designItemService.setPriorityAndUndividedLayer(layers);
|
||||
Map<String, List<String>> priorityAndUndividedLayer = designItemService.setPriorityAndUndividedLayer(layers, null);
|
||||
for (DesignPythonItem detail : item.getItems()) {
|
||||
if (null == detail) {
|
||||
continue;
|
||||
@@ -852,7 +853,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
Map<String, Integer> typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0],
|
||||
d -> Math.abs(d.getPriority()),
|
||||
(existing, replacement) -> replacement));
|
||||
Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
||||
// Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
||||
for (DesignPythonItem detail : item.getItems()) {
|
||||
if (null == detail) {
|
||||
continue;
|
||||
@@ -863,9 +864,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
designItemDetail.setDesignItemId(designItemId);
|
||||
designItemDetail.setCollectionElementId(detail.getElementId());
|
||||
designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
|
||||
if (!detail.getType().equals("Body")) {
|
||||
/*if (!detail.getType().equals("Body")) {
|
||||
designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType()));
|
||||
}
|
||||
}*/
|
||||
|
||||
if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
|
||||
designItemDetail.setPath(detail.getBody_path());
|
||||
@@ -1109,18 +1110,20 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
//修改designItem为like状态
|
||||
designItemService.updateLikeStatus(designLikeDTO.getDesignItemId(), (byte) 1);
|
||||
// 记录喜欢的系统sketch
|
||||
addSystemLikeSketch(designItem);
|
||||
addSystemLikeSketch(designItem, designLikeDTO.getProjectId());
|
||||
// 更新项目更新时间
|
||||
projectService.modifyProjectUpdateTime(designLikeDTO.getProjectId());
|
||||
return new DesignLikeVO(userLikeSortId, userGroupId, groupDetailId, pictureName, userLike.getId(), userLikeSort.getSort());
|
||||
}
|
||||
|
||||
private void addSystemLikeSketch(DesignItem designItem) {
|
||||
public void addSystemLikeSketch(DesignItem designItem, Long projectId) {
|
||||
AuthPrincipalVo userHolder = UserContext.getUserHolder();
|
||||
QueryWrapper<DesignItemDetail> qw = new QueryWrapper<>();
|
||||
qw.lambda().eq(DesignItemDetail::getDesignItemId, designItem.getId());
|
||||
qw.lambda().ne(DesignItemDetail::getType, "Body");
|
||||
List<DesignItemDetail> designItemDetails = designItemDetailMapper.selectList(qw);
|
||||
List<WorkspaceRelStyle> workspaceRelStyles = workspaceRelStyleMapper.selectByProjectId(projectId);
|
||||
|
||||
for (DesignItemDetail designItemDetail : designItemDetails) {
|
||||
if (designItemDetail.getPath().startsWith("aida-sys-image")) {
|
||||
String[] split = designItemDetail.getPath().split("/");
|
||||
@@ -1133,28 +1136,34 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
userBehavior.setCreateTime(LocalDateTime.now());
|
||||
userBehaviorMapper.insert(userBehavior);
|
||||
|
||||
UserPreferenceLogTest userPreferenceLogTest = new UserPreferenceLogTest();
|
||||
userPreferenceLogTest.setPath(designItemDetail.getPath());
|
||||
UserPreference userPreference = new UserPreference();
|
||||
userPreference.setPath(designItemDetail.getPath());
|
||||
SysFile sysFile = sysFileService.getOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUrl, designItemDetail.getPath()));
|
||||
userPreferenceLogTest.setAccountId(userHolder.getId());
|
||||
if (sysFile != null){
|
||||
userPreferenceLogTest.setCategory(sysFile.getLevel3Type().toLowerCase()+"_"+sysFile.getLevel2Type().toLowerCase());
|
||||
userPreferenceLogTest.setSysFileId(sysFile.getId());
|
||||
userPreferenceLogTest.setStyle(sysFile.getStyle());
|
||||
}else {
|
||||
log.error("sysFile not found:{}",designItemDetail.getPath());
|
||||
SendEmailUtil.commonExceptionReminder("url在sysFile里找不到"+designItemDetail.getPath(), new String[]{"litianxiangxtt@163.com"});
|
||||
userPreference.setAccountId(userHolder.getId());
|
||||
if (sysFile != null) {
|
||||
userPreference.setCategory(sysFile.getLevel3Type().toLowerCase() + "_" + sysFile.getLevel2Type().toLowerCase());
|
||||
userPreference.setStyle(sysFile.getStyle());
|
||||
} else {
|
||||
log.error("sysFile not found:{}", designItemDetail.getPath());
|
||||
SendEmailUtil.commonExceptionReminder("url在sysFile里找不到" + designItemDetail.getPath(), new String[]{"litianxiangxtt@163.com"});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// userPreferenceLogTest.setUserLikeGroupId(userLike.getUserLikeGroupId());
|
||||
userPreferenceLogTest.setDataTime(designItemDetail.getCreateDate().toInstant()
|
||||
userPreference.setDataTime(new Date().toInstant()
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime());
|
||||
userPreferenceLogMapper.insert(userPreferenceLogTest);
|
||||
userPreference.setDesignItemId(designItem.getId());
|
||||
userPreference.setProjectId(projectId);
|
||||
if (workspaceRelStyles == null||workspaceRelStyles.isEmpty()) {
|
||||
//查不到记录,style应该是all
|
||||
userPreference.setWorkspaceRelStyleId(0L);
|
||||
} else {
|
||||
userPreference.setWorkspaceRelStyleId(workspaceRelStyles.get(0).getStyleId());
|
||||
}
|
||||
userPreferenceMapper.insert(userPreference);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private List<Long> validateMergeElement(List<CollectionElement> oldElements, List<DesignItemDetail> designItemDetails) {
|
||||
@@ -1322,12 +1331,13 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
d.setScope(o.getPath().startsWith("aida-sys-image") ? "sys" : "user");
|
||||
d.setLevel1Type(converTypeToLevel1(o.getType()));
|
||||
d.setGradient(JSONObject.parseObject(o.getGradientString(), Gradient.class));
|
||||
/*// 取消存储/返回UndividedLayer和UndividedLayerWithSinglePrint字段
|
||||
if (!StringUtil.isNullOrEmpty(o.getUndividedLayer())) {
|
||||
d.setUndividedLayer(minioUtil.getPreSignedUrl(o.getUndividedLayer(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
|
||||
}
|
||||
if (!StringUtil.isNullOrEmpty(o.getUndividedLayerWithSinglePrint())) {
|
||||
d.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(o.getUndividedLayerWithSinglePrint(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
|
||||
}
|
||||
}*/
|
||||
// 根据designItemDetailId获取印花
|
||||
List<DesignItemDetailPrint> prints = designItemDetailPrintService.getByDesignItemDetailId(o.getId(), "print");
|
||||
prints.removeIf(print -> !minioUtil.doesObjectExist(print.getPath()));
|
||||
@@ -1576,6 +1586,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
}
|
||||
}
|
||||
designSinglePrint.setScale(scales);
|
||||
designSinglePrint.setObject(detailPrint.getObject());
|
||||
prints.add(designSinglePrint);
|
||||
} else {
|
||||
// 多个印花
|
||||
@@ -1596,7 +1607,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
scales = Arrays.asList(scale, scale);
|
||||
}
|
||||
}
|
||||
prints.add(new DesignSinglePrint(
|
||||
DesignSinglePrint designSinglePrint = new DesignSinglePrint(
|
||||
print.getLevel2Type(),
|
||||
minioUtil.getPreSignedUrl(print.getPath(), 24 * 60),
|
||||
print.getPath(),
|
||||
@@ -1604,7 +1615,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
scales,
|
||||
print.getAngle(),
|
||||
print.getPriority(),
|
||||
ifSingle));
|
||||
ifSingle);
|
||||
designSinglePrint.setObject(print.getObject());
|
||||
prints.add(designSinglePrint);
|
||||
// }
|
||||
});
|
||||
}
|
||||
@@ -2532,7 +2545,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
Map<String, Integer> typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0],
|
||||
d -> Math.abs(d.getPriority()),
|
||||
(existing, replacement) -> replacement));
|
||||
Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
||||
// Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
|
||||
for (DesignPythonItem detail : item.getItems()) {
|
||||
if (null == detail) {
|
||||
continue;
|
||||
@@ -2543,9 +2556,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
designItemDetail.setDesignItemId(designItemId);
|
||||
designItemDetail.setCollectionElementId(detail.getElementId());
|
||||
designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
|
||||
if (!detail.getType().equals("Body")) {
|
||||
/*if (!detail.getType().equals("Body")) {
|
||||
designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType()));
|
||||
}
|
||||
}*/
|
||||
if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
|
||||
designItemDetail.setPath(detail.getBody_path());
|
||||
//BODY不关联businessId
|
||||
|
||||
300
src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java
Normal file
300
src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java
Normal file
@@ -0,0 +1,300 @@
|
||||
package com.ai.da.service.impl;
|
||||
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.common.enums.AuthenticationOperationTypeEnum;
|
||||
import com.ai.da.common.utils.*;
|
||||
import com.ai.da.mapper.primary.AccountMapper;
|
||||
import com.ai.da.mapper.primary.ContestantMapper;
|
||||
import com.ai.da.mapper.primary.NotificationMapper;
|
||||
import com.ai.da.mapper.primary.entity.Account;
|
||||
import com.ai.da.mapper.primary.entity.Contestant;
|
||||
import com.ai.da.mapper.primary.entity.Notification;
|
||||
import com.ai.da.model.dto.ContestantDTO;
|
||||
import com.ai.da.model.dto.PublishSysNotificationDTO;
|
||||
import com.ai.da.model.vo.CheckOTPVO;
|
||||
import com.ai.da.service.GlobalAwardService;
|
||||
import com.ai.da.service.MessageCenterService;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class GlobalAwardServiceImpl implements GlobalAwardService {
|
||||
|
||||
@Resource
|
||||
private ContestantMapper contestantMapper;
|
||||
|
||||
private final AccountMapper accountMapper;
|
||||
|
||||
private final MessageCenterService messageCenterService;
|
||||
|
||||
private final NotificationMapper notificationMapper;
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
@Value("${file.upload.dir:uploads}")
|
||||
private String uploadDir;
|
||||
|
||||
private static final DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy/MM");
|
||||
|
||||
private static final String tokenCacheKey = AuthenticationOperationTypeEnum.GLOBAL_AWARD.name() + ":";
|
||||
|
||||
@Value("${minio.bucket:contestants}")
|
||||
private String minioBucket;
|
||||
|
||||
@Value("${global.award.link}")
|
||||
private String link;
|
||||
|
||||
@Resource
|
||||
private MinioUtil minioUtil;
|
||||
|
||||
@Override
|
||||
public String uploadPdf(MultipartFile file, String email) throws Exception {
|
||||
validatePdf(file);
|
||||
String path = storeFile(file, email, "pdf");
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadVideo(MultipartFile file, String email) throws Exception {
|
||||
validateVideo(file);
|
||||
String path = storeFile(file, email, "video");
|
||||
return path;
|
||||
}
|
||||
|
||||
private void validatePdf(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new BusinessException("File is empty.");
|
||||
}
|
||||
String ct = file.getContentType();
|
||||
if (ct == null || !ct.toLowerCase().contains("pdf")) {
|
||||
throw new BusinessException("Only PDF files are allowed.");
|
||||
}
|
||||
// size limit example 20MB
|
||||
if (file.getSize() > 20L * 1024 * 1024) {
|
||||
throw new BusinessException("PDF file size exceeds the limit.");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateVideo(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new BusinessException("File is empty.");
|
||||
}
|
||||
String ct = file.getContentType();
|
||||
if (ct == null || !(ct.toLowerCase().contains("mp4") || ct.toLowerCase().contains("video") )) {
|
||||
throw new BusinessException("Invalid video file type.");
|
||||
}
|
||||
// size limit example 100MB
|
||||
if (file.getSize() > 100L * 1024 * 1024) {
|
||||
throw new BusinessException("Video file size exceeds the limit.");
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeEmail(String email) {
|
||||
if (email == null) {
|
||||
return "anonymous";
|
||||
}
|
||||
return email.replaceAll("[^a-zA-Z0-9]", "_");
|
||||
}
|
||||
|
||||
private String storeFile(MultipartFile file, String email, String kind) throws IOException {
|
||||
String normalized = normalizeEmail(email);
|
||||
String datePart = LocalDateTime.now().format(YYYY_MM_DD);
|
||||
String ext = "";
|
||||
String original = file.getOriginalFilename();
|
||||
if (original != null && original.contains(".")) {
|
||||
ext = original.substring(original.lastIndexOf('.'));
|
||||
}
|
||||
String filename = System.currentTimeMillis() + "_" + UUID.randomUUID().toString() + ext;
|
||||
String relativePath = "contestants/" + normalized + "/" + datePart + "/" + filename;
|
||||
|
||||
String uploadedPath = minioUtil.upload(minioBucket, relativePath, file, null);
|
||||
log.info("uploaded via MinioUtil: {}", uploadedPath);
|
||||
return uploadedPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> saveContestant(ContestantDTO request) {
|
||||
Map<String,Object> resp = new HashMap<>();
|
||||
if (request.getEmail() == null) {
|
||||
throw new BusinessException("Email is required.");
|
||||
}
|
||||
|
||||
checkSecurityToken(request.getEmail(), request.getSecureToken());
|
||||
|
||||
QueryWrapper<Contestant> qw = new QueryWrapper<>();
|
||||
qw.eq("email", request.getEmail());
|
||||
Contestant existing = contestantMapper.selectOne(qw);
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (existing == null) {
|
||||
Contestant toInsert = Contestant.builder()
|
||||
.email(request.getEmail())
|
||||
.firstName(request.getFirstName())
|
||||
.lastName(request.getLastName())
|
||||
.gender(request.getGender())
|
||||
.occupation(request.getOccupation())
|
||||
.age(request.getAge())
|
||||
.countryRegionCity(request.getCountryRegionCity())
|
||||
.phoneNumber(request.getPhoneNumber())
|
||||
.designTitle(request.getDesignTitle())
|
||||
.designDescription(request.getDesignDescription())
|
||||
.pdfPath(request.getPdfPath())
|
||||
.videoPath(request.getVideoPath())
|
||||
.createdAt(now)
|
||||
.updatedAt(now)
|
||||
.build();
|
||||
contestantMapper.insert(toInsert);
|
||||
resp.put("success", true);
|
||||
sendSiteMsg(toInsert.getId(), toInsert.getEmail());
|
||||
return resp;
|
||||
} else {
|
||||
// update existing contestant
|
||||
existing.setFirstName(request.getFirstName());
|
||||
existing.setLastName(request.getLastName());
|
||||
existing.setGender(request.getGender());
|
||||
existing.setOccupation(request.getOccupation());
|
||||
existing.setAge(request.getAge());
|
||||
existing.setCountryRegionCity(request.getCountryRegionCity());
|
||||
existing.setPhoneNumber(request.getPhoneNumber());
|
||||
existing.setDesignTitle(request.getDesignTitle());
|
||||
existing.setDesignDescription(request.getDesignDescription());
|
||||
existing.setPdfPath(request.getPdfPath());
|
||||
existing.setVideoPath(request.getVideoPath());
|
||||
existing.setUpdatedAt(now);
|
||||
contestantMapper.updateById(existing);
|
||||
resp.put("success", true);
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContestantDTO getContestantByID(String id) {
|
||||
if (id == null) {
|
||||
throw new BusinessException("id is required.");
|
||||
}
|
||||
Contestant existing = contestantMapper.selectById(id);
|
||||
if (existing == null) {
|
||||
return null;
|
||||
}
|
||||
ContestantDTO dto = new ContestantDTO();
|
||||
// dto.setEmail(existing.getEmail());
|
||||
dto.setFirstName(existing.getFirstName());
|
||||
dto.setLastName(existing.getLastName());
|
||||
dto.setGender(existing.getGender());
|
||||
dto.setOccupation(existing.getOccupation());
|
||||
dto.setAge(existing.getAge());
|
||||
dto.setCountryRegionCity(existing.getCountryRegionCity());
|
||||
dto.setPhoneNumber(existing.getPhoneNumber());
|
||||
dto.setDesignTitle(existing.getDesignTitle());
|
||||
dto.setDesignDescription(existing.getDesignDescription());
|
||||
dto.setPdfPath(existing.getPdfPath());
|
||||
dto.setVideoPath(existing.getVideoPath());
|
||||
return dto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查邮箱是否符合申请要求,发送验证码
|
||||
* @param email AiDA邮箱
|
||||
*/
|
||||
public void checkEmail(String email) {
|
||||
List<Integer> validRole = Arrays.asList(1, 2, 7, 8);
|
||||
// 1. 验证邮箱在aida中有无账号
|
||||
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(Account::getUserEmail, email);
|
||||
List<Account> accounts = accountMapper.selectList(queryWrapper);
|
||||
if (accounts.isEmpty()) {
|
||||
throw new BusinessException("Please register and subscribe to AiDA, then resubmit your application.");
|
||||
}
|
||||
|
||||
// 2. 验证账号是否是付费用户(如果首次提交是,但是修改的时候已经不是了,how?不允许修改吗)
|
||||
if (validRole.contains(accounts.getFirst().getSystemUser())) {
|
||||
String randomVerifyCode = RandomsUtil.generateVerifyCode(100000L, 999999L);
|
||||
LocalCacheUtils.setVerifyCodeCache(
|
||||
AuthenticationOperationTypeEnum.GLOBAL_AWARD.name() + "_" + email, randomVerifyCode);
|
||||
SendEmailUtil.send(email, null,
|
||||
SendEmailUtil.LOGIN_TEMPLATE_ID, randomVerifyCode);
|
||||
} else {
|
||||
throw new BusinessException("Please subscribe to AiDA, then resubmit your application.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证验证码是否正确
|
||||
* @param email 邮箱
|
||||
* @param otp 一次性验证码
|
||||
* @return 临时token和之前提交的表单内容
|
||||
*/
|
||||
public CheckOTPVO checkCode(String email, String otp) {
|
||||
String otpCache = LocalCacheUtils.getVerifyCodeCache(AuthenticationOperationTypeEnum.GLOBAL_AWARD.name() + "_" + email);
|
||||
assert otpCache != null;
|
||||
if (otpCache.equals(otp)) {
|
||||
// 1. 生成唯一token
|
||||
String secureToken = UUID.randomUUID().toString().replace("-", "");
|
||||
redisUtil.addToString(tokenCacheKey + email, secureToken, 3 * 24 * 60 * 60L);
|
||||
|
||||
return new CheckOTPVO(secureToken, getContestantByID(email));
|
||||
} else {
|
||||
throw new BusinessException("Verification code is incorrect. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
public void checkSecurityToken(String email, String securityToken) {
|
||||
String key = tokenCacheKey + email;
|
||||
if (StringUtils.isBlank(securityToken)) {
|
||||
log.error("security token 缺失");
|
||||
throw new BusinessException("Please complete email verification first.");
|
||||
}
|
||||
|
||||
String tokenCache = redisUtil.getFromString(key);
|
||||
if (StringUtils.isBlank(tokenCache)) {
|
||||
log.error("security token 过期");
|
||||
throw new BusinessException("Email verification has expired. Please verify again.");
|
||||
} else if (!tokenCache.equals(securityToken)){
|
||||
log.error("security token 与缓存不符");
|
||||
throw new BusinessException("Identity verification failed. Please complete email verification first.");
|
||||
}
|
||||
}
|
||||
|
||||
// 发送站内信
|
||||
public void sendSiteMsg(String applicationId, String email) {
|
||||
Long userId;
|
||||
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(Account::getUserEmail, email);
|
||||
List<Account> accounts = accountMapper.selectList(queryWrapper);
|
||||
if (accounts.isEmpty()) {
|
||||
throw new BusinessException("Please register and subscribe to AiDA, then resubmit your application.");
|
||||
}else {
|
||||
userId = accounts.get(0).getId();
|
||||
}
|
||||
PublishSysNotificationDTO sysNotificationDTO = new PublishSysNotificationDTO();
|
||||
Notification notification = new Notification();
|
||||
notification.setType("system");
|
||||
notification.setReceiverId(userId);
|
||||
sysNotificationDTO.setTitle("System Notification 系统通知");
|
||||
// todo
|
||||
sysNotificationDTO.setContent(link + applicationId);
|
||||
notification.setContent(JSON.toJSONString(sysNotificationDTO));
|
||||
notification.setIsRead(0);
|
||||
notification.setCreateTime(LocalDateTime.now());
|
||||
notificationMapper.insert(notification);
|
||||
// 这里推送消息是在接受到视频生成结束后发生的,所以UserContext中没有用户信息
|
||||
messageCenterService.pushMessage("system", userId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -194,6 +194,8 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
||||
if (!type.equals("system")) {
|
||||
// 个人未读消息
|
||||
count = getUnreadCountByType(type, receiverId);
|
||||
} else if (Objects.isNull(receiverId)) {
|
||||
count = 1L;
|
||||
} else {
|
||||
// 系统未读消息
|
||||
count = getUnreadSystemNotification(receiverId);
|
||||
@@ -253,12 +255,14 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
|
||||
// 计算总的系统通知数量
|
||||
QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(Notification::getType, "system")
|
||||
.gt(Notification::getCreateTime, account.getCreateDate())
|
||||
.and(wrapper -> wrapper
|
||||
.isNull(Notification::getReceiverId)
|
||||
.or()
|
||||
.eq(Notification::getReceiverId, receiverId)
|
||||
);
|
||||
if (Objects.nonNull(account)) {
|
||||
queryWrapper.lambda().gt(Notification::getCreateTime, account.getCreateDate());
|
||||
}
|
||||
Long totalSysCount = baseMapper.selectCount(queryWrapper);
|
||||
|
||||
// 计算单个用户读了多少条系统数据
|
||||
|
||||
@@ -258,6 +258,11 @@ public class PanToneServiceImpl extends ServiceImpl<PanToneMapper, PanTone> impl
|
||||
d.setH(getRgbByHsvBatchDTO.getH());
|
||||
d.setS(getRgbByHsvBatchDTO.getS());
|
||||
d.setV(getRgbByHsvBatchDTO.getV());
|
||||
// 不使用数据库中存储的RGB值,使用通过hsv计算得到的RGB值
|
||||
int[] rgb = PantoneUtils.hsvToRgb(d.getH(), d.getS(), d.getV());
|
||||
d.setR(rgb[0]);
|
||||
d.setG(rgb[1]);
|
||||
d.setB(rgb[2]);
|
||||
}
|
||||
});
|
||||
Map<Integer, PantoneVO> valueToPantoneVo = templateResposne.stream().collect(Collectors.toMap(
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.ai.da.service.SubscriptionPlanService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
@@ -28,6 +29,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -37,11 +39,9 @@ import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
|
||||
import static com.ai.da.mapper.primary.entity.Account.SystemRole.EDUCATION_SUB;
|
||||
import static com.ai.da.mapper.primary.entity.SubscriptionPlan.SubscriptionStatus.ACTIVE;
|
||||
import static com.ai.da.mapper.primary.entity.SubscriptionPlan.SubscriptionStatus.PENDING;
|
||||
|
||||
@@ -78,9 +78,9 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
||||
}
|
||||
|
||||
baseMapper.insert(subscriptionPlan);
|
||||
if (subscriptionPlanDTO.getStatus().equals(SubscriptionPlan.SubscriptionStatus.ACTIVE.name())) {
|
||||
if (subscriptionPlan.getStatus().equals(SubscriptionPlan.SubscriptionStatus.ACTIVE.name())) {
|
||||
// 执行一次激活扫描器
|
||||
activeSubscriptionPlan();
|
||||
activeSubscriptionPlan(subscriptionPlan.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,52 +104,227 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
||||
if (account.getSystemUser().equals(8) || account.getSystemUser().equals(6)) {
|
||||
throw new BusinessException("Sub-accounts.cannot.be.admins");
|
||||
}
|
||||
|
||||
// 保证订阅计划绑定的管理员所属组织的唯一性
|
||||
checkAdminCrossOrg(subscriptionPlanDTO.getAdminAccId(), subscriptionPlanDTO.getOrganizationId());
|
||||
}
|
||||
|
||||
// 判断指定的管理员是否已绑定其他组织的订阅计划
|
||||
private void checkAdminCrossOrg(Long adminAccId, Long organizationId) {
|
||||
QueryWrapper<SubscriptionPlan> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(SubscriptionPlan::getAdminAccId, adminAccId)
|
||||
.ne(SubscriptionPlan::getOrganizationId, organizationId);
|
||||
Long count = baseMapper.selectCount(queryWrapper);
|
||||
if (count > 0) {
|
||||
throw new BusinessException("administrator.user.is.already.bound.to.different.organization");
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 到期时间、积分总量、已使用积分量
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void updatePlan(UpdateSubscriptionPlanDTO updateDTO) {
|
||||
if (Objects.isNull(updateDTO.getId())) {
|
||||
public void updatePlan(UpdateSubscriptionPlanDTO dto) {
|
||||
if (dto.getId() == null) {
|
||||
throw new BusinessException("id.cannot.be.empty");
|
||||
}
|
||||
|
||||
SubscriptionPlan subscriptionPlan = baseMapper.selectById(updateDTO.getId());
|
||||
if (Objects.isNull(subscriptionPlan)) {
|
||||
SubscriptionPlan plan = baseMapper.selectById(dto.getId());
|
||||
if (plan == null) {
|
||||
throw new BusinessException("unknown.subscription.plan");
|
||||
}
|
||||
|
||||
if (Objects.nonNull(updateDTO.getCurrentPeriodStart()) && !updateDTO.getCurrentPeriodStart().equals(subscriptionPlan.getCurrentPeriodStart())) {
|
||||
subscriptionPlan.setCurrentPeriodStart(updateDTO.getCurrentPeriodStart());
|
||||
}
|
||||
boolean activateToday = false;
|
||||
|
||||
if (Objects.nonNull(updateDTO.getCurrentPeriodEnd()) && !updateDTO.getCurrentPeriodEnd().equals(subscriptionPlan.getCurrentPeriodEnd())) {
|
||||
subscriptionPlan.setCurrentPeriodEnd(updateDTO.getCurrentPeriodEnd());
|
||||
}
|
||||
activateToday = handlePeriodStart(dto, plan);
|
||||
handlePeriodEnd(dto, plan);
|
||||
handleAccountNum(dto, plan);
|
||||
handleCreditLimit(dto, plan);
|
||||
handleBasicInfo(dto, plan);
|
||||
|
||||
if (Objects.nonNull(updateDTO.getAccountNum()) && !updateDTO.getAccountNum().equals(subscriptionPlan.getAccountNum())) {
|
||||
subscriptionPlan.setAccountNum(updateDTO.getAccountNum());
|
||||
}
|
||||
|
||||
if (Objects.nonNull(updateDTO.getCreditLimit()) && !updateDTO.getCreditLimit().equals(subscriptionPlan.getCreditLimit())) {
|
||||
subscriptionPlan.setCreditLimit(updateDTO.getCreditLimit());
|
||||
}
|
||||
|
||||
if (Objects.nonNull(updateDTO.getAdminAccId()) && !updateDTO.getAdminAccId().equals(subscriptionPlan.getAdminAccId())) {
|
||||
subscriptionPlan.setAdminAccId(updateDTO.getAdminAccId());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(updateDTO.getName()) && !updateDTO.getName().equals(subscriptionPlan.getName())) {
|
||||
subscriptionPlan.setName(updateDTO.getName());
|
||||
}
|
||||
|
||||
subscriptionPlan.setUpdateTime(LocalDateTime.now());
|
||||
updateById(subscriptionPlan);
|
||||
plan.setUpdateTime(LocalDateTime.now());
|
||||
updateById(plan);
|
||||
|
||||
postUpdateProcess(plan, activateToday);
|
||||
}
|
||||
|
||||
public void updatePlan() {
|
||||
// ===================== 字段处理 =====================
|
||||
|
||||
/**
|
||||
* 处理开始时间,返回是否需要当天激活
|
||||
*/
|
||||
private boolean handlePeriodStart(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
|
||||
Long newStart = dto.getCurrentPeriodStart();
|
||||
if (newStart == null || newStart.equals(plan.getCurrentPeriodStart())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ACTIVE.name().equals(plan.getStatus())) {
|
||||
throw new BusinessException(
|
||||
"only.subscription.plans.with.a.PENDING.status.can.have.their.start.time.modified"
|
||||
);
|
||||
}
|
||||
|
||||
plan.setCurrentPeriodStart(newStart);
|
||||
return isToday(newStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理结束时间(只能延长)
|
||||
*/
|
||||
private void handlePeriodEnd(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
|
||||
Long newEnd = dto.getCurrentPeriodEnd();
|
||||
if (newEnd == null || newEnd.equals(plan.getCurrentPeriodEnd())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newEnd < plan.getCurrentPeriodEnd()) {
|
||||
throw new BusinessException(
|
||||
"the.subscription.end.date.can.be.extended.only.not.reduced"
|
||||
);
|
||||
}
|
||||
|
||||
plan.setCurrentPeriodEnd(newEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理账号数量
|
||||
*/
|
||||
private void handleAccountNum(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
|
||||
Integer newAccountNum = dto.getAccountNum();
|
||||
if (newAccountNum == null || newAccountNum.equals(plan.getAccountNum())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newAccountNum < plan.getAccountNum()) {
|
||||
long usedSubAccounts = countExistingSubAccounts(plan.getId());
|
||||
if (newAccountNum < usedSubAccounts + 1) {
|
||||
throw new BusinessException(
|
||||
"total.sub-account.quota.cannot.be.lower.than.existing.sub-accounts"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
plan.setAccountNum(newAccountNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理积分上限
|
||||
*/
|
||||
private void handleCreditLimit(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
|
||||
BigDecimal newLimit = dto.getCreditLimit();
|
||||
if (newLimit == null || newLimit.equals(plan.getCreditLimit())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newLimit.compareTo(plan.getCreditUsage()) < 0) {
|
||||
throw new BusinessException(
|
||||
"the.credit.limit.set.cannot.be.lower.than.the.amount.of.credits.already.used"
|
||||
);
|
||||
}
|
||||
|
||||
plan.setCreditLimit(newLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础字段
|
||||
*/
|
||||
private void handleBasicInfo(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
|
||||
if (dto.getAdminAccId() != null
|
||||
&& !dto.getAdminAccId().equals(plan.getAdminAccId())) {
|
||||
// 保证订阅计划绑定的管理员所属组织的唯一性
|
||||
checkAdminCrossOrg(dto.getAdminAccId(), plan.getOrganizationId());
|
||||
plan.setAdminAccId(dto.getAdminAccId());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(dto.getName())
|
||||
&& !dto.getName().equals(plan.getName())) {
|
||||
plan.setName(dto.getName());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(dto.getCountryOrRegion())
|
||||
&& !dto.getCountryOrRegion().equals(plan.getCountryOrRegion())) {
|
||||
plan.setCountryOrRegion(dto.getCountryOrRegion());
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 更新后处理 =====================
|
||||
|
||||
private void postUpdateProcess(SubscriptionPlan plan, boolean activateToday) {
|
||||
if (ACTIVE.name().equals(plan.getStatus())) {
|
||||
syncAdminAndSubAccounts(plan);
|
||||
return;
|
||||
}
|
||||
|
||||
if (activateToday) {
|
||||
activeSubscriptionPlan(plan.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 账号同步 =====================
|
||||
|
||||
private void syncAdminAndSubAccounts(SubscriptionPlan plan) {
|
||||
Account admin = findActiveAdmin(plan);
|
||||
if (admin != null) {
|
||||
syncAdminAccount(admin, plan);
|
||||
}
|
||||
syncSubAccounts(plan);
|
||||
}
|
||||
|
||||
private Account findActiveAdmin(SubscriptionPlan plan) {
|
||||
return accountMapper.selectOne(
|
||||
new QueryWrapper<Account>().lambda()
|
||||
.eq(Account::getId, plan.getAdminAccId())
|
||||
.eq(Account::getSubscriptionPlanId, plan.getId())
|
||||
);
|
||||
}
|
||||
|
||||
private void syncAdminAccount(Account admin, SubscriptionPlan plan) {
|
||||
long planEndMillis = toMillis(plan.getCurrentPeriodEnd());
|
||||
|
||||
if (!Objects.equals(admin.getValidEndTime(), planEndMillis)) {
|
||||
admin.setValidEndTime(planEndMillis);
|
||||
}
|
||||
|
||||
if (admin.getCreditsUsageLimit().compareTo(plan.getCreditLimit()) != 0) {
|
||||
// 这里计算修改前后的差值,上限增长,则差为正,上限下降,则差为负;
|
||||
BigDecimal delta = plan.getCreditLimit()
|
||||
.subtract(admin.getCreditsUsageLimit());
|
||||
|
||||
// 因为管理员的积分中可能包含自己购买的积分,所以这里直接将差值添加到管理员的credit中
|
||||
admin.setCredits(admin.getCredits().add(delta));
|
||||
admin.setCreditsUsageLimit(plan.getCreditLimit());
|
||||
}
|
||||
|
||||
accountMapper.updateById(admin);
|
||||
}
|
||||
|
||||
private void syncSubAccounts(SubscriptionPlan plan) {
|
||||
accountMapper.update(
|
||||
null,
|
||||
new UpdateWrapper<Account>().lambda()
|
||||
.set(Account::getValidEndTime, toMillis(plan.getCurrentPeriodEnd()))
|
||||
.eq(Account::getSubscriptionPlanId, plan.getId())
|
||||
.eq(Account::getSystemUser, EDUCATION_SUB.getCode())
|
||||
);
|
||||
}
|
||||
|
||||
// ===================== 辅助方法 =====================
|
||||
|
||||
private long countExistingSubAccounts(Long planId) {
|
||||
return accountMapper.selectCount(
|
||||
new QueryWrapper<Account>().lambda()
|
||||
.eq(Account::getSubscriptionPlanId, planId)
|
||||
.eq(Account::getSystemUser, EDUCATION_SUB.getCode())
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isToday(Long timestampSeconds) {
|
||||
return timestampSeconds >= getTodayStartTimestamp()
|
||||
&& timestampSeconds < getTodayEndTimestamp();
|
||||
}
|
||||
|
||||
private long toMillis(Long seconds) {
|
||||
return seconds * 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -167,6 +342,12 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
||||
queryWrapper.lambda().in(SubscriptionPlan::getStatus, subscriptionPlanPageQuery.getStatus());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(subscriptionPlanPageQuery.getCountryOrRegion())){
|
||||
queryWrapper.lambda().like(SubscriptionPlan::getCountryOrRegion, subscriptionPlanPageQuery.getCountryOrRegion());
|
||||
}
|
||||
|
||||
queryWrapper.lambda().orderByAsc(SubscriptionPlan::getCurrentPeriodStart);
|
||||
|
||||
return baseMapper.selectList(queryWrapper);
|
||||
|
||||
}
|
||||
@@ -221,6 +402,9 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
||||
wrapper.in("sp.status", query.getStatus());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(query.getCountryOrRegion())) {
|
||||
wrapper.like("sp.country_or_region", query.getCountryOrRegion());
|
||||
}
|
||||
// 按创建时间倒序排序
|
||||
wrapper.ne("sp.is_deleted", 1)
|
||||
.orderByDesc("sp.create_time");
|
||||
@@ -398,7 +582,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
||||
}
|
||||
|
||||
// 检查是否在有效期内
|
||||
if (plan.getCurrentPeriodEnd() != null && isExpired(plan.getCurrentPeriodEnd())) {
|
||||
if (plan.getCurrentPeriodEnd() != null && !isExpired(plan.getCurrentPeriodEnd())) {
|
||||
throw new BusinessException("valid.subscription.period");
|
||||
}
|
||||
}
|
||||
@@ -418,18 +602,28 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
|
||||
return currentPeriodEnd < currentTimestamp;
|
||||
}
|
||||
|
||||
public void activeSubscriptionPlan() {
|
||||
public void activeSubscriptionPlan(Long planId) {
|
||||
log.info("开始执行订阅计划生效检查...");
|
||||
|
||||
// 1. 扫描所有的订阅计划的开始时间currentPeriodStart,找出今天开始生效的计划
|
||||
List<SubscriptionPlan> todayActivePlans = findTodayActivePlans();
|
||||
// 支持按id激活
|
||||
List<SubscriptionPlan> todayActivePlans = new ArrayList<>();
|
||||
if (Objects.nonNull(planId)) {
|
||||
SubscriptionPlan subscriptionPlan = baseMapper.selectById(planId);
|
||||
if (Objects.nonNull(subscriptionPlan)){
|
||||
todayActivePlans.add(subscriptionPlan);
|
||||
}
|
||||
} else {
|
||||
// 1. 扫描所有的订阅计划的开始时间currentPeriodStart,找出今天开始生效的计划
|
||||
todayActivePlans = findTodayActivePlans();
|
||||
|
||||
if (CollectionUtils.isEmpty(todayActivePlans)) {
|
||||
log.info("今日没有需要生效的订阅计划");
|
||||
return;
|
||||
if (CollectionUtils.isEmpty(todayActivePlans)) {
|
||||
log.info("今日没有需要生效的订阅计划");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("发现{}个今日生效的订阅计划", todayActivePlans.size());
|
||||
}
|
||||
|
||||
log.info("发现{}个今日生效的订阅计划", todayActivePlans.size());
|
||||
|
||||
// 2. 处理每个今天开始生效的订阅计划
|
||||
for (SubscriptionPlan plan : todayActivePlans) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.ai.da.model.vo.DesignPythonOutfitVO;
|
||||
import com.ai.da.model.vo.TDesignPythonOutfitDetailVO;
|
||||
import com.ai.da.service.ITDesignPythonOutfitDetailService;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
@@ -67,6 +68,13 @@ public class TDesignPythonOutfitDetailServiceImpl extends ServiceImpl<TDesignPyt
|
||||
designPythonOutfitVO.setScale(modifyScale(detail.getScale()));
|
||||
designPythonOutfitVO.setOffset(StringUtil.isNullOrEmpty(detail.getOffset()) ? Arrays.asList(0L, 0L) : parseLongList(detail.getOffset()));
|
||||
designPythonOutfitVO.setPriority(Math.abs(detail.getPriority()));
|
||||
if (detail.getTranspose() != null) {
|
||||
List<Integer> transposeList = JSONArray.parseArray(detail.getTranspose(), Integer.class);
|
||||
designPythonOutfitVO.setTranspose(transposeList.stream().mapToInt(Integer::intValue).toArray());
|
||||
} else {
|
||||
designPythonOutfitVO.setTranspose(null);
|
||||
}
|
||||
designPythonOutfitVO.setRotate(detail.getRotate());
|
||||
// designPythonOutfitVO.setOffset(CollectionUtil.isEmpty(offset) ? Arrays.asList(0L, 0L) : offset);
|
||||
|
||||
/*if (!StringUtil.isNullOrEmpty(detail.getImageSize())){
|
||||
|
||||
93
src/main/java/com/ai/da/service/upload/UploadService.java
Normal file
93
src/main/java/com/ai/da/service/upload/UploadService.java
Normal file
@@ -0,0 +1,93 @@
|
||||
package com.ai.da.service.upload;
|
||||
|
||||
import com.ai.da.model.dto.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
||||
/**
|
||||
* 分片上传服务接口
|
||||
* 提供PDF和视频文件的分片上传、断点续传功能
|
||||
*/
|
||||
public interface UploadService {
|
||||
|
||||
// ===== PDF上传相关 =====
|
||||
|
||||
/**
|
||||
* 初始化PDF上传任务
|
||||
* @param request 初始化请求
|
||||
* @return 上传任务信息
|
||||
*/
|
||||
UploadTask initPdfUpload(UploadInitRequest request);
|
||||
|
||||
/**
|
||||
* 上传PDF分片
|
||||
* @param uploadId 上传任务ID
|
||||
* @param chunk 分片文件
|
||||
* @param chunkIndex 分片索引
|
||||
* @param totalChunks 总分片数
|
||||
* @return 分片上传结果
|
||||
*/
|
||||
UploadChunkResponse uploadPdfChunk(String uploadId, MultipartFile chunk,
|
||||
int chunkIndex, int totalChunks);
|
||||
|
||||
/**
|
||||
* 完成PDF上传(异步合并并上传到MinIO)
|
||||
* @param uploadId 上传任务ID
|
||||
* @param fileName 文件名
|
||||
* @param totalSize 文件总大小
|
||||
* @return 完成上传结果
|
||||
*/
|
||||
UploadCompleteResponse completePdfUpload(String uploadId, String fileName, long totalSize, String email, String secureToken);
|
||||
|
||||
/**
|
||||
* 查询PDF上传状态
|
||||
* @param uploadId 上传任务ID
|
||||
* @return 上传状态信息
|
||||
*/
|
||||
UploadStatusResponse getPdfUploadStatus(String uploadId);
|
||||
|
||||
|
||||
// ===== 视频上传相关 =====
|
||||
|
||||
/**
|
||||
* 初始化视频上传任务
|
||||
* @param request 初始化请求
|
||||
* @return 上传任务信息
|
||||
*/
|
||||
UploadTask initVideoUpload(UploadInitRequest request);
|
||||
|
||||
/**
|
||||
* 上传视频分片
|
||||
* @param uploadId 上传任务ID
|
||||
* @param chunk 分片文件
|
||||
* @param chunkIndex 分片索引
|
||||
* @param totalChunks 总分片数
|
||||
* @return 分片上传结果
|
||||
*/
|
||||
UploadChunkResponse uploadVideoChunk(String uploadId, MultipartFile chunk,
|
||||
int chunkIndex, int totalChunks);
|
||||
|
||||
/**
|
||||
* 完成视频上传(异步合并并上传到MinIO)
|
||||
* @param uploadId 上传任务ID
|
||||
* @param fileName 文件名
|
||||
* @param totalSize 文件总大小
|
||||
* @return 完成上传结果
|
||||
*/
|
||||
UploadCompleteResponse completeVideoUpload(String uploadId, String fileName, long totalSize, String email, String secureToken);
|
||||
|
||||
/**
|
||||
* 查询视频上传状态
|
||||
* @param uploadId 上传任务ID
|
||||
* @return 上传状态信息
|
||||
*/
|
||||
UploadStatusResponse getVideoUploadStatus(String uploadId);
|
||||
|
||||
|
||||
// ===== 通用功能 =====
|
||||
|
||||
/**
|
||||
* 清理过期上传任务
|
||||
*/
|
||||
void cleanupExpiredUploads();
|
||||
}
|
||||
111
src/main/java/com/ai/da/service/upload/UploadTask.java
Normal file
111
src/main/java/com/ai/da/service/upload/UploadTask.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package com.ai.da.service.upload;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 上传任务实体类
|
||||
* 用于管理分片上传的状态和元数据
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class UploadTask {
|
||||
|
||||
/**
|
||||
* 上传任务唯一标识
|
||||
*/
|
||||
private String uploadId;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 文件类型 (pdf/video)
|
||||
*/
|
||||
private String fileType;
|
||||
|
||||
/**
|
||||
* 文件总大小(字节)
|
||||
*/
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 用户邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 总分片数
|
||||
*/
|
||||
private Integer totalChunks;
|
||||
|
||||
/**
|
||||
* 分片大小(字节)
|
||||
*/
|
||||
private Integer chunkSize;
|
||||
|
||||
/**
|
||||
* 已上传分片索引集合
|
||||
*/
|
||||
private Set<Integer> uploadedChunks;
|
||||
|
||||
/**
|
||||
* 上传状态
|
||||
*/
|
||||
private UploadStatus status;
|
||||
|
||||
/**
|
||||
* 任务创建时间
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 任务过期时间
|
||||
*/
|
||||
private LocalDateTime expiresAt;
|
||||
|
||||
/**
|
||||
* 最终文件在MinIO中的路径
|
||||
*/
|
||||
private String finalPath;
|
||||
|
||||
/**
|
||||
* 上传状态枚举
|
||||
*/
|
||||
public enum UploadStatus {
|
||||
/**
|
||||
* 已初始化,等待上传分片
|
||||
*/
|
||||
INITIATED,
|
||||
|
||||
/**
|
||||
* 正在上传分片
|
||||
*/
|
||||
UPLOADING,
|
||||
|
||||
/**
|
||||
* 正在处理文件(合并分片、上传到存储)
|
||||
*/
|
||||
PROCESSING,
|
||||
|
||||
/**
|
||||
* 上传完成
|
||||
*/
|
||||
COMPLETED,
|
||||
|
||||
/**
|
||||
* 上传失败
|
||||
*/
|
||||
FAILED,
|
||||
|
||||
/**
|
||||
* 任务过期
|
||||
*/
|
||||
EXPIRED
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
package com.ai.da.service.upload.impl;
|
||||
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.common.utils.MinioUtil;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.service.upload.UploadService;
|
||||
import com.ai.da.service.upload.UploadTask;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.minio.PutObjectArgs;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 分片上传服务实现类
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class UploadServiceImpl implements UploadService {
|
||||
|
||||
// ===== 配置参数 =====
|
||||
|
||||
@Value("${file.upload.temp.dir:temp/uploads}")
|
||||
private String tempDir;
|
||||
|
||||
// PDF分片大小:1MB
|
||||
@Value("${file.upload.chunk.size.pdf:1048576}")
|
||||
private int pdfChunkSize;
|
||||
|
||||
// 视频分片大小:2MB
|
||||
@Value("${file.upload.chunk.size.video:2097152}")
|
||||
private int videoChunkSize;
|
||||
|
||||
// 文件大小限制
|
||||
@Value("${file.upload.max.size.pdf:20971520}") // PDF: 20MB
|
||||
private long maxPdfSize;
|
||||
|
||||
@Value("${file.upload.max.size.video:104857600}") // 视频: 100MB
|
||||
private long maxVideoSize;
|
||||
|
||||
@Resource
|
||||
private MinioUtil minioUtil;
|
||||
@Value("${minio.bucketName.globalAward:global-award}")
|
||||
private String minioBucket;
|
||||
|
||||
@Resource
|
||||
private com.ai.da.service.GlobalAwardService globalAwardService;
|
||||
|
||||
// 内存存储上传任务状态
|
||||
private final ConcurrentHashMap<String, UploadTask> uploadTasks = new ConcurrentHashMap<>();
|
||||
|
||||
// JSON序列化工具
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 应用启动时加载现有上传任务
|
||||
*/
|
||||
@PostConstruct
|
||||
public void loadExistingTasks() {
|
||||
try {
|
||||
Path tempPath = Paths.get(tempDir);
|
||||
if (!Files.exists(tempPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Files.list(tempPath)
|
||||
.filter(Files::isDirectory)
|
||||
.forEach(uploadDir -> {
|
||||
try {
|
||||
String uploadId = uploadDir.getFileName().toString();
|
||||
Path metadataPath = uploadDir.resolve("metadata.json");
|
||||
|
||||
if (Files.exists(metadataPath)) {
|
||||
String json = Files.readString(metadataPath);
|
||||
UploadTask task = objectMapper.readValue(json, UploadTask.class);
|
||||
|
||||
// 检查任务是否已过期
|
||||
if (task.getExpiresAt().isAfter(LocalDateTime.now())) {
|
||||
uploadTasks.put(uploadId, task);
|
||||
log.info("加载现有上传任务: uploadId={}, status={}", uploadId, task.getStatus());
|
||||
} else {
|
||||
// 清理过期任务
|
||||
cleanupTempFiles(uploadId);
|
||||
log.info("清理过期上传任务: uploadId={}", uploadId);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("加载上传任务失败: {}", uploadDir.getFileName(), e);
|
||||
}
|
||||
});
|
||||
|
||||
log.info("成功加载 {} 个现有上传任务", uploadTasks.size());
|
||||
} catch (Exception e) {
|
||||
log.error("加载现有上传任务时发生错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== PDF上传实现 =====
|
||||
|
||||
@Override
|
||||
public UploadTask initPdfUpload(UploadInitRequest request) {
|
||||
// 验证安全令牌
|
||||
globalAwardService.checkSecurityToken(request.getEmail(), request.getSecureToken());
|
||||
|
||||
// 验证PDF文件
|
||||
validatePdfFile(request);
|
||||
|
||||
// 创建上传任务
|
||||
String uploadId = UUID.randomUUID().toString();
|
||||
int totalChunks = (int) Math.ceil((double) request.getFileSize() / pdfChunkSize);
|
||||
|
||||
UploadTask task = createUploadTask(request, uploadId, totalChunks, pdfChunkSize, "pdf");
|
||||
|
||||
// 创建临时目录并保存任务状态
|
||||
createTempDirectory(uploadId);
|
||||
uploadTasks.put(uploadId, task);
|
||||
saveTaskMetadata(task);
|
||||
|
||||
log.info("PDF上传任务初始化完成: uploadId={}, totalChunks={}, fileSize={}",
|
||||
uploadId, totalChunks, request.getFileSize());
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadChunkResponse uploadPdfChunk(String uploadId, MultipartFile chunk,
|
||||
int chunkIndex, int totalChunks) {
|
||||
// 验证任务状态
|
||||
UploadTask task = validateAndGetTask(uploadId, "pdf");
|
||||
|
||||
// 保存分片到本地
|
||||
saveChunkToLocal(chunk, uploadId, chunkIndex);
|
||||
|
||||
// 更新任务进度
|
||||
updateTaskProgress(task, chunkIndex);
|
||||
|
||||
log.debug("PDF分片上传完成: uploadId={}, chunkIndex={}, size={}",
|
||||
uploadId, chunkIndex, chunk.getSize());
|
||||
|
||||
return UploadChunkResponse.builder()
|
||||
.chunkIndex(chunkIndex)
|
||||
.uploaded(true)
|
||||
.size(chunk.getSize())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadCompleteResponse completePdfUpload(String uploadId, String fileName, long totalSize, String email, String secureToken) {
|
||||
// 验证安全令牌
|
||||
globalAwardService.checkSecurityToken(email, secureToken);
|
||||
|
||||
UploadTask task = validateAndGetTask(uploadId, "pdf");
|
||||
|
||||
log.info("开始PDF文件合并: uploadId={}, fileName={}", uploadId, fileName);
|
||||
|
||||
try {
|
||||
// 1. 合并所有分片
|
||||
Path mergedFile = mergeChunks(task);
|
||||
|
||||
// 2. 上传到MinIO
|
||||
String finalPath = uploadToMinio(task, mergedFile, "pdf");
|
||||
|
||||
// 3. 更新任务状态并清理
|
||||
completeTask(task, finalPath);
|
||||
cleanupTempFiles(uploadId);
|
||||
|
||||
log.info("PDF上传完成: uploadId={}, finalPath={}", uploadId, finalPath);
|
||||
|
||||
return buildCompleteResponse(task, finalPath, totalSize);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("PDF上传失败: uploadId={}", uploadId, e);
|
||||
task.setStatus(UploadTask.UploadStatus.FAILED);
|
||||
saveTaskMetadata(task);
|
||||
throw new BusinessException("File merge failed. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadStatusResponse getPdfUploadStatus(String uploadId) {
|
||||
UploadTask task = uploadTasks.get(uploadId);
|
||||
if (task == null) {
|
||||
throw new BusinessException("Upload task not found.");
|
||||
}
|
||||
|
||||
if (!"pdf".equals(task.getFileType())) {
|
||||
throw new BusinessException("Task type mismatch.");
|
||||
}
|
||||
|
||||
// 计算上传进度
|
||||
double progress = task.getTotalChunks() > 0 ?
|
||||
(double) task.getUploadedChunks().size() / task.getTotalChunks() * 100 : 0;
|
||||
|
||||
long uploadedSize = task.getUploadedChunks().size() * task.getChunkSize();
|
||||
|
||||
return UploadStatusResponse.builder()
|
||||
.uploadId(uploadId)
|
||||
.status(task.getStatus().name().toLowerCase())
|
||||
.progress(Math.min(progress, 100.0))
|
||||
.uploadedChunks(new HashSet<>(task.getUploadedChunks()))
|
||||
.totalChunks(task.getTotalChunks())
|
||||
.totalSize(task.getFileSize())
|
||||
.uploadedSize(Math.min(uploadedSize, task.getFileSize()))
|
||||
.build();
|
||||
}
|
||||
|
||||
// ===== 视频上传实现 =====
|
||||
|
||||
@Override
|
||||
public UploadTask initVideoUpload(UploadInitRequest request) {
|
||||
// 验证安全令牌
|
||||
globalAwardService.checkSecurityToken(request.getEmail(), request.getSecureToken());
|
||||
|
||||
// 验证视频文件
|
||||
validateVideoFile(request);
|
||||
|
||||
// 创建上传任务
|
||||
String uploadId = UUID.randomUUID().toString();
|
||||
int totalChunks = (int) Math.ceil((double) request.getFileSize() / videoChunkSize);
|
||||
|
||||
UploadTask task = createUploadTask(request, uploadId, totalChunks, videoChunkSize, "video");
|
||||
|
||||
// 创建临时目录并保存任务状态
|
||||
createTempDirectory(uploadId);
|
||||
uploadTasks.put(uploadId, task);
|
||||
saveTaskMetadata(task);
|
||||
|
||||
log.info("视频上传任务初始化完成: uploadId={}, totalChunks={}, fileSize={}",
|
||||
uploadId, totalChunks, request.getFileSize());
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadChunkResponse uploadVideoChunk(String uploadId, MultipartFile chunk,
|
||||
int chunkIndex, int totalChunks) {
|
||||
// 验证任务状态
|
||||
UploadTask task = validateAndGetTask(uploadId, "video");
|
||||
|
||||
// 保存分片到本地
|
||||
saveChunkToLocal(chunk, uploadId, chunkIndex);
|
||||
|
||||
// 更新任务进度
|
||||
updateTaskProgress(task, chunkIndex);
|
||||
|
||||
log.debug("视频分片上传完成: uploadId={}, chunkIndex={}, size={}",
|
||||
uploadId, chunkIndex, chunk.getSize());
|
||||
|
||||
return UploadChunkResponse.builder()
|
||||
.chunkIndex(chunkIndex)
|
||||
.uploaded(true)
|
||||
.size(chunk.getSize())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadCompleteResponse completeVideoUpload(String uploadId, String fileName, long totalSize, String email, String secureToken) {
|
||||
// 验证安全令牌
|
||||
globalAwardService.checkSecurityToken(email, secureToken);
|
||||
|
||||
UploadTask task = validateAndGetTask(uploadId, "video");
|
||||
|
||||
log.info("开始视频文件合并: uploadId={}, fileName={}", uploadId, fileName);
|
||||
|
||||
try {
|
||||
// 1. 合并所有分片
|
||||
Path mergedFile = mergeChunks(task);
|
||||
|
||||
// 2. 上传到MinIO
|
||||
String finalPath = uploadToMinio(task, mergedFile, "video");
|
||||
|
||||
// 3. 更新任务状态并清理
|
||||
completeTask(task, finalPath);
|
||||
cleanupTempFiles(uploadId);
|
||||
|
||||
log.info("视频上传完成: uploadId={}, finalPath={}", uploadId, finalPath);
|
||||
|
||||
return buildCompleteResponse(task, finalPath, totalSize);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("视频上传失败: uploadId={}", uploadId, e);
|
||||
task.setStatus(UploadTask.UploadStatus.FAILED);
|
||||
saveTaskMetadata(task);
|
||||
throw new BusinessException("File merge failed. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadStatusResponse getVideoUploadStatus(String uploadId) {
|
||||
UploadTask task = uploadTasks.get(uploadId);
|
||||
if (task == null) {
|
||||
throw new BusinessException("Upload task not found.");
|
||||
}
|
||||
|
||||
if (!"video".equals(task.getFileType())) {
|
||||
throw new BusinessException("Task type mismatch.");
|
||||
}
|
||||
|
||||
// 计算上传进度
|
||||
double progress = task.getTotalChunks() > 0 ?
|
||||
(double) task.getUploadedChunks().size() / task.getTotalChunks() * 100 : 0;
|
||||
|
||||
long uploadedSize = task.getUploadedChunks().size() * task.getChunkSize();
|
||||
|
||||
return UploadStatusResponse.builder()
|
||||
.uploadId(uploadId)
|
||||
.status(task.getStatus().name().toLowerCase())
|
||||
.progress(Math.min(progress, 100.0))
|
||||
.uploadedChunks(new HashSet<>(task.getUploadedChunks()))
|
||||
.totalChunks(task.getTotalChunks())
|
||||
.totalSize(task.getFileSize())
|
||||
.uploadedSize(Math.min(uploadedSize, task.getFileSize()))
|
||||
.build();
|
||||
}
|
||||
|
||||
// ===== 核心辅助方法 =====
|
||||
|
||||
/**
|
||||
* 验证PDF文件
|
||||
*/
|
||||
private void validatePdfFile(UploadInitRequest request) {
|
||||
if (!"application/pdf".equals(request.getFileType())) {
|
||||
throw new BusinessException("Only PDF files are allowed.");
|
||||
}
|
||||
if (request.getFileSize() > maxPdfSize) {
|
||||
throw new BusinessException("PDF file size cannot exceed " + (maxPdfSize / 1024 / 1024) + "MB.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证视频文件
|
||||
*/
|
||||
private void validateVideoFile(UploadInitRequest request) {
|
||||
if (request.getFileType() == null ||
|
||||
(!request.getFileType().contains("mp4") &&
|
||||
!request.getFileType().contains("video") &&
|
||||
!request.getFileType().contains("avi"))) {
|
||||
throw new BusinessException("Unsupported video format.");
|
||||
}
|
||||
if (request.getFileSize() > maxVideoSize) {
|
||||
throw new BusinessException("Video file size cannot exceed " + (maxVideoSize / 1024 / 1024) + "MB.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建上传任务
|
||||
*/
|
||||
private UploadTask createUploadTask(UploadInitRequest request, String uploadId,
|
||||
int totalChunks, int chunkSize, String fileType) {
|
||||
return UploadTask.builder()
|
||||
.uploadId(uploadId)
|
||||
.fileName(request.getFileName())
|
||||
.fileType(fileType)
|
||||
.fileSize(request.getFileSize())
|
||||
.email(request.getEmail())
|
||||
.totalChunks(totalChunks)
|
||||
.chunkSize(chunkSize)
|
||||
.uploadedChunks(new HashSet<>())
|
||||
.status(UploadTask.UploadStatus.INITIATED)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.expiresAt(LocalDateTime.now().plusHours(24))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时目录
|
||||
*/
|
||||
private void createTempDirectory(String uploadId) {
|
||||
try {
|
||||
Path uploadDir = Paths.get(tempDir, uploadId, "chunks");
|
||||
Files.createDirectories(uploadDir);
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException("Failed to create temporary directory.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并获取上传任务
|
||||
*/
|
||||
private UploadTask validateAndGetTask(String uploadId, String expectedType) {
|
||||
UploadTask task = uploadTasks.get(uploadId);
|
||||
if (task == null) {
|
||||
throw new BusinessException("Upload task not found.");
|
||||
}
|
||||
|
||||
if (task.getExpiresAt().isBefore(LocalDateTime.now())) {
|
||||
task.setStatus(UploadTask.UploadStatus.EXPIRED);
|
||||
throw new BusinessException("Upload task has expired.");
|
||||
}
|
||||
|
||||
if (!expectedType.equals(task.getFileType())) {
|
||||
throw new BusinessException("Task type mismatch.");
|
||||
}
|
||||
|
||||
if (task.getStatus() == UploadTask.UploadStatus.COMPLETED ||
|
||||
task.getStatus() == UploadTask.UploadStatus.FAILED) {
|
||||
throw new BusinessException("Task has already been completed or failed.");
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存分片到本地
|
||||
*/
|
||||
private void saveChunkToLocal(MultipartFile chunk, String uploadId, int chunkIndex) {
|
||||
try {
|
||||
Path chunkPath = Paths.get(tempDir, uploadId, "chunks", "chunk_" + chunkIndex);
|
||||
Files.copy(chunk.getInputStream(), chunkPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException("Failed to save file chunk.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务进度
|
||||
*/
|
||||
private void updateTaskProgress(UploadTask task, int chunkIndex) {
|
||||
task.getUploadedChunks().add(chunkIndex);
|
||||
task.setStatus(UploadTask.UploadStatus.UPLOADING);
|
||||
saveTaskMetadata(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并分片文件(零拷贝方式)
|
||||
*/
|
||||
private Path mergeChunks(UploadTask task) throws IOException {
|
||||
Path mergedFile = Files.createTempFile("merge_", "_" + task.getFileName());
|
||||
|
||||
try (FileChannel outputChannel = FileChannel.open(mergedFile, StandardOpenOption.WRITE)) {
|
||||
for (int i = 0; i < task.getTotalChunks(); i++) {
|
||||
Path chunkPath = Paths.get(tempDir, task.getUploadId(), "chunks", "chunk_" + i);
|
||||
try (FileChannel inputChannel = FileChannel.open(chunkPath, StandardOpenOption.READ)) {
|
||||
// 零拷贝传输,提升性能
|
||||
inputChannel.transferTo(0, inputChannel.size(), outputChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mergedFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到MinIO
|
||||
*/
|
||||
private String uploadToMinio(UploadTask task, Path mergedFile, String fileType) throws Exception {
|
||||
// 生成MinIO路径: contestants/{email}/{date}/{filename}
|
||||
String normalizedEmail = normalizeEmail(task.getEmail());
|
||||
String datePart = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
|
||||
String ext = getFileExtension(task.getFileName());
|
||||
String filename = System.currentTimeMillis() + "_" + UUID.randomUUID().toString() + ext;
|
||||
String relativePath = "contestants/" + normalizedEmail + "/" + datePart + "/" + filename;
|
||||
|
||||
// 上传文件
|
||||
try (FileInputStream fis = new FileInputStream(mergedFile.toFile())) {
|
||||
minioUtil.getMinioClient().putObject(
|
||||
PutObjectArgs.builder()
|
||||
.bucket(minioBucket)
|
||||
.object(relativePath)
|
||||
.stream(fis, mergedFile.toFile().length(), -1)
|
||||
.contentType(getContentType(fileType))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成任务
|
||||
*/
|
||||
private void completeTask(UploadTask task, String finalPath) {
|
||||
task.setStatus(UploadTask.UploadStatus.COMPLETED);
|
||||
task.setFinalPath(finalPath);
|
||||
saveTaskMetadata(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建完成响应
|
||||
*/
|
||||
private UploadCompleteResponse buildCompleteResponse(UploadTask task, String finalPath, long totalSize) {
|
||||
// todo:URL是逻辑url
|
||||
String fileUrl = minioBucket + "/" + finalPath;
|
||||
|
||||
return UploadCompleteResponse.builder()
|
||||
.filePath(finalPath)
|
||||
.fileUrl(fileUrl)
|
||||
.fileSize(totalSize)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存任务元数据
|
||||
*/
|
||||
private void saveTaskMetadata(UploadTask task) {
|
||||
try {
|
||||
Path metadataPath = Paths.get(tempDir, task.getUploadId(), "metadata.json");
|
||||
String json = objectMapper.writeValueAsString(task);
|
||||
Files.writeString(metadataPath, json);
|
||||
} catch (IOException e) {
|
||||
log.warn("保存任务元数据失败: uploadId={}", task.getUploadId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理临时文件
|
||||
*/
|
||||
private void cleanupTempFiles(String uploadId) {
|
||||
try {
|
||||
Path uploadDir = Paths.get(tempDir, uploadId);
|
||||
if (Files.exists(uploadDir)) {
|
||||
Files.walk(uploadDir)
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.forEach(path -> {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (IOException e) {
|
||||
log.warn("删除临时文件失败: {}", path);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("清理临时文件失败: uploadId={}", uploadId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期上传任务(每小时执行一次)
|
||||
*/
|
||||
@Scheduled(fixedDelay = 3600000) // 1小时
|
||||
public void cleanupExpiredUploads() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
uploadTasks.entrySet().removeIf(entry -> {
|
||||
UploadTask task = entry.getValue();
|
||||
if (task.getExpiresAt().isBefore(now)) {
|
||||
cleanupTempFiles(task.getUploadId());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 工具方法 =====
|
||||
|
||||
/**
|
||||
* 标准化邮箱地址
|
||||
*/
|
||||
private String normalizeEmail(String email) {
|
||||
if (email == null) {
|
||||
return "anonymous";
|
||||
}
|
||||
return email.replaceAll("[^a-zA-Z0-9]", "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
*/
|
||||
private String getFileExtension(String fileName) {
|
||||
if (fileName != null && fileName.contains(".")) {
|
||||
return fileName.substring(fileName.lastIndexOf('.'));
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件MIME类型
|
||||
*/
|
||||
private String getContentType(String fileType) {
|
||||
switch (fileType.toLowerCase()) {
|
||||
case "pdf":
|
||||
return "application/pdf";
|
||||
case "video":
|
||||
return "video/mp4";
|
||||
default:
|
||||
return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ spring.security.jwtExpiration=8640000000
|
||||
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\
|
||||
/api/account/**,/api/element/**,/api/python/**,/api/design/**,/api/history/**,/api/library/**,/api/third/party/**,/api/generate/**,/api/workspace/**,/api/classification/**,\
|
||||
/api/product/**,/api/ali-pay/**,/api/order-info/**,/api/paypal/**,/api/credits/**,/api/inquiry/**,/api/tasks/**,/api/python/prepareForSR,/api/alipay-hk/**,/api/portfolio/**,\
|
||||
/api/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**, /api/subscription_plan/**
|
||||
/api/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**, /api/subscription_plan/**,/api/global-award/**
|
||||
spring.security.authApi=/auth/login
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ minio.bucketName.gradient=aida-gradient
|
||||
minio.bucketName.modifiedSketch=aida-modified-sketch
|
||||
minio.bucketName.slogan=aida-slogan
|
||||
minio.bucketName.partialDesign=aida-partial-design
|
||||
minio.bucketName.globalAward=global-award
|
||||
redirect_url=http://18.167.251.121:7788
|
||||
|
||||
spring.rabbitmq.host=18.167.251.121
|
||||
@@ -158,4 +159,26 @@ google.client.id=157095842121-kdd1fdf8m8nudvj9sprstb2k2prnf9e4.apps.googleuserco
|
||||
#google.client.secret=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
|
||||
google.client.secret=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
|
||||
google.redirect.uri=https://develop.api.aida.com.hk/api/third/party/auth/google_callback
|
||||
design.callback.url=https://develop.api.aida.com.hk/api/third/party/receiveDesignResults
|
||||
design.callback.url=https://develop.api.aida.com.hk/api/third/party/receiveDesignResults
|
||||
|
||||
# ===== 分片上传配置 =====
|
||||
|
||||
# 临时文件目录
|
||||
file.upload.temp.dir=temp/uploads
|
||||
|
||||
# 分片大小配置
|
||||
# PDF分片大小:1MB
|
||||
file.upload.chunk.size.pdf=1048576
|
||||
# 视频分片大小:2MB
|
||||
file.upload.chunk.size.video=2097152
|
||||
|
||||
# 文件大小限制
|
||||
# PDF最大文件大小:20MB
|
||||
file.upload.max.size.pdf=20971520
|
||||
# 视频最大文件大小:100MB
|
||||
file.upload.max.size.video=104857600
|
||||
|
||||
# 上传任务过期时间(小时)
|
||||
file.upload.task.expiry.hours=24
|
||||
|
||||
global.award.link=https://develop.aida.com.hk/award/contestants?id=
|
||||
@@ -22,7 +22,7 @@ spring.security.jwtExpiration=604800000
|
||||
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\
|
||||
/api/account/**,/api/element/**,/api/python/**,/api/design/**,/api/history/**,/api/library/**,/api/third/party/**,/api/generate/**,/api/workspace/**,/api/classification/**,\
|
||||
/api/product/**,/api/ali-pay/**,/api/order-info/**,/api/paypal/**,/api/credits/**,/api/inquiry/**,/api/tasks/**,/api/python/prepareForSR,/api/alipay-hk/**,/api/portfolio/**,\
|
||||
/api/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**, /api/subscription_plan/**
|
||||
/api/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**, /api/subscription_plan/**,/api/global-award/**
|
||||
spring.security.authApi=/auth/login
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ minio.bucketName.gradient=aida-gradient
|
||||
minio.bucketName.modifiedSketch=aida-modified-sketch
|
||||
minio.bucketName.slogan=aida-slogan
|
||||
minio.bucketName.partialDesign=aida-partial-design
|
||||
minio.bucketName.globalAward=global-award
|
||||
redirect_url=http://18.167.251.121:7788
|
||||
|
||||
spring.rabbitmq.host=18.167.251.121
|
||||
@@ -156,4 +157,26 @@ FREEPIK_API_KEY=FPSX94e5917d376a4facb87dabbaa0319c72
|
||||
google.client.id=29310152396-nnsd3h533fld665oguu8ovrt1nukmt46.apps.googleusercontent.com
|
||||
google.client.secret=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
|
||||
google.redirect.uri=https://www.api.aida.com.hk/api/third/party/auth/google_callback
|
||||
design.callback.url=https://api.aida.com.hk/api/third/party/receiveDesignResults
|
||||
design.callback.url=https://api.aida.com.hk/api/third/party/receiveDesignResults
|
||||
|
||||
# ===== 分片上传配置 =====
|
||||
|
||||
# 临时文件目录
|
||||
file.upload.temp.dir=temp/uploads
|
||||
|
||||
# 分片大小配置
|
||||
# PDF分片大小:1MB
|
||||
file.upload.chunk.size.pdf=1048576
|
||||
# 视频分片大小:2MB
|
||||
file.upload.chunk.size.video=2097152
|
||||
|
||||
# 文件大小限制
|
||||
# PDF最大文件大小:20MB
|
||||
file.upload.max.size.pdf=20971520
|
||||
# 视频最大文件大小:100MB
|
||||
file.upload.max.size.video=104857600
|
||||
|
||||
# 上传任务过期时间(小时)
|
||||
file.upload.task.expiry.hours=24
|
||||
|
||||
global.award.link=https://www.aida.com.hk/award/contestants?id=
|
||||
@@ -0,0 +1,19 @@
|
||||
<?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.ai.da.mapper.primary.WorkspaceRelStyleMapper">
|
||||
|
||||
<select id="selectByProjectId" resultType="com.ai.da.mapper.primary.entity.WorkspaceRelStyle">
|
||||
SELECT
|
||||
wrs.workspace_id,
|
||||
wrs.style_id
|
||||
FROM
|
||||
workspace_rel_style wrs
|
||||
INNER JOIN
|
||||
workspace w ON wrs.workspace_id = w.id
|
||||
WHERE
|
||||
w.project_id = #{projectId}
|
||||
AND w.is_deleted = 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -209,6 +209,12 @@ end.time.must.be.later.than.the.start.time=The subscription end time must be lat
|
||||
please.specify.the.organizationId=Please specify the organizationId.
|
||||
switch.failed.sub-account.not.under.your.active.subscription=Switch failed. Sub-account not under your active subscription.
|
||||
Sub-accounts.cannot.be.admins=Sub-accounts in a subscription cannot be designated as admins.
|
||||
only.subscription.plans.with.a.PENDING.status.can.have.their.start.time.modified=Only subscription plans with a PENDING status can have their start time modified.
|
||||
the.subscription.end.date.can.be.extended.only.not.reduced=The subscription end date can be extended only, not reduced.
|
||||
total.sub-account.quota.cannot.be.lower.than.existing.sub-accounts=Total sub-account quota cannot be lower than existing sub-accounts.
|
||||
the.credit.limit.set.cannot.be.lower.than.the.amount.of.credits.already.used=The credit limit set cannot be lower than the amount of credits already used.
|
||||
administrator.user.is.already.bound.to.different.organization=This administrator user is already bound to a subscription plan of a different organization.
|
||||
required.partialDesign='partialDesign' (base64 or path) is required when updating an individual outfit.
|
||||
|
||||
# 可能会报异常
|
||||
# Informative:
|
||||
|
||||
@@ -205,6 +205,12 @@ end.time.must.be.later.than.the.start.time=订阅结束时间必须晚于开始
|
||||
please.specify.the.organizationId=请指定organizationId
|
||||
switch.failed.sub-account.not.under.your.active.subscription=切换失败,该子账号不属于您当前管理的订阅计划
|
||||
Sub-accounts.cannot.be.admins=在订阅中的子账号不能被指定为管理员
|
||||
only.subscription.plans.with.a.PENDING.status.can.have.their.start.time.modified=只有PENDING状态的订阅计划可以修改订阅开始时间
|
||||
the.subscription.end.date.can.be.extended.only.not.reduced=订阅的到期时间不能缩短,只能延长
|
||||
total.sub-account.quota.cannot.be.lower.than.existing.sub-accounts=设置的子账号总数量不能低于现存已添加的子账号数量
|
||||
the.credit.limit.set.cannot.be.lower.than.the.amount.of.credits.already.used=设置的积分上限不能低于已使用的积分量
|
||||
administrator.user.is.already.bound.to.different.organization=该管理员用户已与其他组织的订阅计划绑定
|
||||
required.partialDesign=修改单套搭配必须提供'partialDesign'(base64 或 path)
|
||||
|
||||
# 可能会报异常
|
||||
# Informative:
|
||||
|
||||
@@ -27,20 +27,20 @@ paypal.webhook_id=1D107312EX592781K
|
||||
##### Stripe
|
||||
|
||||
# developer
|
||||
#stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2
|
||||
stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2
|
||||
# dev 端点
|
||||
#stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w
|
||||
stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w
|
||||
# local 端点
|
||||
#stripe.webhook-sign-secret=whsec_TJcMSnAkh4uktrNY1M6Iy8XaVze4Rzqm
|
||||
|
||||
# kim - test
|
||||
stripe.private-key=sk_test_51LwPrxH7nPZ8bkrNj67TFD7sxucaTANs1lf0KGSu1QSJfxYXcnigq2wTaZyZzST7y0fMbhhvaJZ4LjjFhr95M83a00eXrmOTL0
|
||||
#stripe.private-key=sk_test_51LwPrxH7nPZ8bkrNj67TFD7sxucaTANs1lf0KGSu1QSJfxYXcnigq2wTaZyZzST7y0fMbhhvaJZ4LjjFhr95M83a00eXrmOTL0
|
||||
# prod 端点
|
||||
#stripe.webhook-sign-secret=whsec_GoyVEAaBtuGD5Rt55z83JnPnLDAZTN3u
|
||||
# local 端点
|
||||
#stripe.webhook-sign-secret=whsec_NvwM3hDQiN5GXclYOYekE9IKHLjmROF8
|
||||
# dev 端点
|
||||
stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL
|
||||
#stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL
|
||||
|
||||
# kim - live
|
||||
#stripe.private-key=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
|
||||
|
||||
Reference in New Issue
Block a user