192 Commits

Author SHA1 Message Date
litianxiang
e4940019bf 框选适配py 2026-03-27 15:19:38 +08:00
litianxiang
0da66ff210 print t2i模型替换 2026-03-27 15:16:33 +08:00
litianxiang
76eeb2be53 moodboard基础模型修改 2026-03-25 10:39:22 +08:00
litianxiang
cb6f94d2d4 py api fix 2026-03-25 10:19:06 +08:00
litianxiang
28656c44c8 FIX FLUX2 2026-03-24 16:24:43 +08:00
litianxiang
6757a89d04 Pattern模式参数fix 2026-03-24 15:54:53 +08:00
litianxiang
9be1a1e307 加锁解决不同线程读取前还未保存的问题 2026-03-24 15:49:16 +08:00
litianxiang
2168978f61 print pattern也改为flux2 2026-03-24 15:32:06 +08:00
litianxiang
54466b935d debug 2026-03-24 15:23:33 +08:00
litianxiang
c970ebe691 debug 2026-03-24 15:15:59 +08:00
litianxiang
1c5a3a12b9 debug 2026-03-24 15:04:40 +08:00
litianxiang
6e06000083 debug 2026-03-24 14:46:01 +08:00
litianxiang
dea2b3be42 debug 2026-03-24 14:29:08 +08:00
litianxiang
bcf51aea23 debug 2026-03-24 14:20:39 +08:00
litianxiang
0c9d5404c6 flux2失败状态判断 2026-03-24 14:05:27 +08:00
litianxiang
93429839c0 本地flux改为flux2 2026-03-24 13:39:38 +08:00
litianxiang
27859c3e28 Merge remote-tracking branch 'origin/release/3.1' into dev/3.1_release_merge 2026-03-23 14:11:33 +08:00
litianxiang
f02c0930a6 日志切面(controller层报错打印) 2026-03-23 13:56:47 +08:00
litianxiang
d57bb83b25 Merge remote-tracking branch 'origin/release/3.1' into release/3.1 2026-03-23 13:50:44 +08:00
731e34f133 TO DEV 2026-03-23 13:38:10 +08:00
75eca8d6ba Merge branch 'release/3.1' into dev/3.1_release_merge
# Conflicts:
#	src/main/java/com/ai/da/python/PythonService.java
2026-03-23 13:22:18 +08:00
3e53401f76 TASK:返回符合查询条件的金额总计 2026-03-23 11:55:07 +08:00
litianxiang
b6a068ebcd SKETCHBOARD传入的text改为获取第一个,为分割获取style的方式 2026-03-23 11:50:24 +08:00
litianxiang
dc291ea086 加入非空校验 2026-03-14 01:50:39 +08:00
litianxiang
2e846e671a romantic风格缺少下装fix 2026-03-13 10:06:26 +08:00
litianxiang
a5093311f9 当style为洛丽塔时无sketch和谷歌风控问题FIX 2026-03-12 19:06:54 +08:00
litianxiang
aed338a6d7 当style为洛丽塔时无sketch和谷歌风控问题FIX 2026-03-12 18:59:17 +08:00
litianxiang
8bdb49d25c 当style为洛丽塔时无sketch和谷歌风控问题FIX 2026-03-12 18:49:57 +08:00
litianxiang
5d53a8cd42 当style为洛丽塔时无sketch和谷歌风控问题FIX 2026-03-12 18:49:28 +08:00
litianxiang
61b7f3072f 当style为洛丽塔时无sketch和谷歌风控问题FIX 2026-03-12 18:36:21 +08:00
litianxiang
a1f489f3a1 比赛url修改 2026-03-12 17:39:05 +08:00
litianxiang
fc3fd877a8 transpose和rotate获取位置修改 2026-03-05 16:58:17 +08:00
litianxiang
fc72d2c430 transpose和rotate获取位置修改 2026-03-05 13:29:14 +08:00
litianxiang
1ac01dd090 测试token恢复 2026-02-25 16:36:06 +08:00
litianxiang
3bbdf7c672 fix:按编号导出参赛选手文件 2026-02-09 10:33:25 +08:00
litianxiang
0646484fba 按编号导出参赛选手文件 2026-02-09 10:21:40 +08:00
litianxiang
96b8613741 映射temp到服务器 2026-02-06 17:29:42 +08:00
litianxiang
cf30226a51 映射temp到服务器 2026-02-06 17:24:43 +08:00
litianxiang
3c15a3ff68 映射temp到服务器 2026-02-06 17:21:07 +08:00
litianxiang
0c904be227 测试临时token 2026-02-06 11:42:34 +08:00
litianxiang
7759b56123 Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev/3.1_release_merge 2026-02-05 17:39:15 +08:00
litianxiang
d5bfaa8822 fix:允许图生图不带提示词 2026-02-05 17:38:38 +08:00
967c0cbc01 Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-02-05 16:55:24 +08:00
417e34b41a BUGFIX:1.首次design没有使用library和生成的印花2.design overall印花太小 2026-02-05 16:54:45 +08:00
litianxiang
d51aa84647 fix:参赛者根据链接返回文件参数 2026-02-05 09:41:10 +08:00
litianxiang
5895bc6ab6 Revert "fix:参赛者根据链接返回文件参数"
This reverts commit 3301869f20.
2026-02-05 09:40:35 +08:00
litianxiang
3301869f20 fix:参赛者根据链接返回文件参数 2026-02-05 09:40:15 +08:00
litianxiang
1ec42f4ad5 fix:参赛者id逻辑更改 2026-02-04 17:20:22 +08:00
cc506ff7e9 Merge branch 'dev/3.1_release_merge' into release/3.1 2026-02-04 17:05:59 +08:00
litianxiang
f2d43f06f4 Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev/3.1_release_merge 2026-02-04 15:28:08 +08:00
litianxiang
9251df49f8 比赛新增文件大小和视频时长 2026-02-04 15:27:51 +08:00
430156f4e8 Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-02-04 14:48:38 +08:00
litianxiang
d1123aedcc fix:导出为页面下载 2026-02-04 14:45:11 +08:00
8c007077a3 BUGFIX: detail中的merge模式下没有存储partialDesign的图片 2026-02-04 14:43:29 +08:00
litianxiang
d63b4b4e63 fix:参赛选手加入编号 2026-02-04 14:03:30 +08:00
litianxiang
b826f0bf39 参赛选手加入编号,增加导出功能 2026-02-04 13:41:16 +08:00
litianxiang
1decd8e258 参赛选手加入编号,增加导出功能 2026-02-04 13:41:09 +08:00
litianxiang
1286e84488 Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev-ltx 2026-02-04 13:21:19 +08:00
a252fdf7f9 BUGFIX: detail中的default模式报错 2026-02-03 16:56:15 +08:00
807d802178 TASK:motion 参数数据类型变更 2026-02-02 15:30:04 +08:00
53f1b548be CONFIG 2026-02-02 15:28:54 +08:00
45dd78032a BUGFIX: 1.token过期,重新登录无法解决 2.motion生成参数数据类型变更 2026-02-02 15:04:27 +08:00
c160da5132 BUGFIX: token过期,重新登录无法解决 2026-02-02 14:57:32 +08:00
b23faeeee2 Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-02-02 13:32:55 +08:00
67789abca4 TASK:getAllPose id的数据类型改为整型 2026-02-02 13:32:27 +08:00
1c78d66aab Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-30 17:14:50 +08:00
528bc69923 BUGFIX: design single merge模式下取消传递print及elements等元素 2026-01-30 17:10:37 +08:00
litianxiang
22880d128d Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev-ltx 2026-01-30 15:40:25 +08:00
9c56a102cc Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-29 14:38:20 +08:00
2f59fe074f BUGFIX: 管理员修改用户身份为游客 2026-01-29 14:37:54 +08:00
9c61b1c8fe Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-29 10:12:47 +08:00
e30fdf7401 BUGFIX:模特上传,事务管理不统一导致library出现孤儿数据 2026-01-29 10:10:21 +08:00
ba2d10afbc paymentMethodConfiguration切换 2026-01-28 15:41:52 +08:00
6146112d04 TO PROD 2026-01-27 16:39:39 +08:00
412550df27 BUGFIX:管理员查design频次与chart中获取到的数量有区别 2026-01-27 14:59:39 +08:00
497421e7fe TO DEV 2026-01-27 10:15:36 +08:00
891527426c Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev/3.1_release_merge
# Conflicts:
#	src/main/java/com/ai/da/service/impl/DesignServiceImpl.java
2026-01-26 14:49:38 +08:00
litianxiang
3e334d7956 Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev-ltx 2026-01-26 11:16:46 +08:00
litianxiang
8f0d0953b2 Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev/3.1_release_merge 2026-01-26 11:15:38 +08:00
f5c3621a5d bugfix: design like 2026-01-23 22:45:40 +08:00
litianxiang
9a1a0045e0 fix:like报错 2026-01-23 22:40:30 +08:00
6223c8e994 brandDNA 2026-01-23 22:25:01 +08:00
67bbee49fd Merge branch 'dev/3.1_release_merge' into release/3.1 2026-01-23 21:20:26 +08:00
ad62ceb32a Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-23 21:02:04 +08:00
082afe9e94 gradiant 置空 2026-01-23 20:57:48 +08:00
49288c3a31 TO Prod 2026-01-23 16:10:55 +08:00
81624e36db Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev/3.1_release_merge 2026-01-23 15:12:15 +08:00
a526b122d1 Merge branch 'release/3.1' into dev/3.1_release_merge 2026-01-23 15:11:45 +08:00
litianxiang
d882b2e817 Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2026-01-23 15:05:22 +08:00
litianxiang
ebf6427d42 fix:用户特征存入逻辑错误 2026-01-23 15:04:59 +08:00
77fe03d361 Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-23 11:46:31 +08:00
7a44d67dbf BUGFIX: 系统消息发布 广播时消息数量错误 2026-01-23 11:46:08 +08:00
55ce2c6c7e Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-23 10:54:35 +08:00
a426caaca3 BUGFIX: 系统消息发布 2026-01-23 10:54:03 +08:00
7cb7ce2836 Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-22 16:56:11 +08:00
8e075f1da4 BUGFIX: 通过hsv批量获取潘通信息,替换rgb 2026-01-22 16:55:00 +08:00
litianxiang
0f0fde2a3e Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2026-01-22 14:28:37 +08:00
litianxiang
8c6389a1f6 删除不用的字段 2026-01-22 14:28:10 +08:00
652f82b6a4 Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-22 13:56:44 +08:00
7ca2528dcf BUGFIX: design后未存储undivided layers 2026-01-22 13:56:07 +08:00
litianxiang
a7800913d2 Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2026-01-22 13:51:42 +08:00
litianxiang
1eaec64ff4 fix:GlobalAward读取配置错误 2026-01-22 13:51:13 +08:00
e603952332 Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-22 11:37:25 +08:00
2bc8b8ef96 BUGFIX: single design的渐变色未存储 2026-01-22 11:36:43 +08:00
0ce968b919 BUGFIX: 用户登录时的有效期验证异常抛出导致事务回滚,用户信息修改失败 2026-01-22 10:37:23 +08:00
litianxiang
dfc9ae4db2 GlobalAward站内信url修改 2026-01-21 16:50:01 +08:00
litianxiang
a3505c6d95 GlobalAward站内信url修改 2026-01-21 15:09:45 +08:00
litianxiang
6db0afd515 GlobalAward保存成功发送站内信,根据url可跳转且召回已填写资料 2026-01-21 14:59:41 +08:00
litianxiang
b1e6183dd1 GlobalAward接口token验证,id更换为uuid 2026-01-21 14:34:43 +08:00
30d08356c0 Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-21 14:14:17 +08:00
64cc29f456 TASK:Global Award邮箱验证 2026-01-21 14:13:33 +08:00
litianxiang
2b3e12a11c GlobalAward MINIO配置 2026-01-21 11:38:38 +08:00
litianxiang
d4a4724f61 GlobalAward拦截器配置 2026-01-21 10:35:22 +08:00
litianxiang
ba6e2bd24c GlobalAward拦截器配置 2026-01-21 10:21:17 +08:00
litianxiang
a38895b028 GlobalAward拦截器配置 2026-01-21 10:13:11 +08:00
litianxiang
69a95e66ca GlobalAward上传文件 2026-01-20 16:37:46 +08:00
litianxiang
40518cab37 Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev-ltx
# Conflicts:
#	src/main/java/com/ai/da/controller/GlobalAwardController.java
#	src/main/java/com/ai/da/model/dto/ContestantDTO.java
#	src/main/resources/application-dev.properties
#	src/main/resources/application-prod.properties
2026-01-20 16:20:41 +08:00
litianxiang
46d61cb73f GlobalAward上传文件 2026-01-20 15:58:27 +08:00
08f20fd1fe Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-20 13:24:49 +08:00
d7edc166b3 TASK:Global Award邮箱验证 2026-01-20 13:14:50 +08:00
litianxiang
79ad02f66b fix:style为all时,like报错 2026-01-19 16:50:14 +08:00
litianxiang
5e261b55c7 Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev/3.1_release_merge 2026-01-19 16:41:02 +08:00
litianxiang
bc92fcbaf4 Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2026-01-19 16:40:58 +08:00
litianxiang
c6aec917c2 fix:style为all时,like报错 2026-01-19 16:40:28 +08:00
6bc500e78f Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-16 17:17:25 +08:00
4c43b98c02 TASK:DB中PartialDesign为空时,取undivided_layer作为merge_image_path 2026-01-16 17:17:04 +08:00
litianxiang
5bae785a9f Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2026-01-16 16:37:49 +08:00
litianxiang
7b619aa4cb GlobalAward首次提交 2026-01-16 16:37:03 +08:00
c93ad6daa9 Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-16 16:24:38 +08:00
0047be7a03 BUGFIX:PartialDesign传空时,先从数据库获取原数据 2026-01-16 16:23:56 +08:00
4ef209cfd4 Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-16 14:28:27 +08:00
a19751b4b7 BUGFIX:designType参数校验;print数据验空 2026-01-16 14:28:04 +08:00
litianxiang
bb0e5a4263 Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2026-01-16 11:04:52 +08:00
litianxiang
9e9df5367d fix:接口SegAnything返回值处理 2026-01-16 11:04:18 +08:00
ba8a2c52de Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev/3.1_release_merge 2026-01-15 17:36:37 +08:00
39d8c7efcf TASK:取消存储/返回UndividedLayer和UndividedLayerWithSinglePrint字段 2026-01-15 17:35:55 +08:00
litianxiang
401910901a Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge
# Conflicts:
#	src/main/java/com/ai/da/python/PythonService.java
2026-01-15 17:21:30 +08:00
litianxiang
3f5ce6e0e7 接口SegAnything返回值处理 2026-01-15 17:17:08 +08:00
0787025151 TASK:merge 模式返回mask 2026-01-15 14:07:18 +08:00
08b26872ff Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2026-01-14 14:28:05 +08:00
5bbf1326bb TASK:design single部分cv操作前置,新增merge | default两种designType 2026-01-14 14:26:47 +08:00
litianxiang
c5e27cd220 Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2026-01-14 13:55:01 +08:00
litianxiang
112e9c3bc9 对接前端和py接口SegAnything 2026-01-14 13:31:33 +08:00
litianxiang
ce95cb5080 Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2026-01-12 14:42:02 +08:00
litianxiang
71211bfbc3 修改存入userPreference表的时间方式 2026-01-12 14:41:24 +08:00
72ad977dcb BUGFIX: 获取近期新用户图表数据允许userType为null 2026-01-12 11:55:43 +08:00
litianxiang
6400e79929 Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2026-01-09 14:49:25 +08:00
litianxiang
dd8c72f7d7 切换用户sketch点赞记录存储方式;新增镜像和角度字段,存储前端需要的object 2026-01-09 10:14:46 +08:00
13151b65f5 TASK:限制同一个管理员不允许绑定不同组织的订阅计划 2026-01-07 15:24:38 +08:00
9f523d5953 TASK:分页获取所有用户id,添加按邮箱模糊查询 2026-01-07 11:26:39 +08:00
4879cfeb60 BUGFIX: 2026-01-07 09:53:58 +08:00
9e252b16ef BUGFIX: 2026-01-06 17:36:12 +08:00
e64add14af BUGFIX:更新订阅计划时根据业务需要对参数进行判断并在需要时更新管理员信息 2026-01-06 17:29:33 +08:00
3beb27e491 TASK:获取用户id信息做分页;订阅计划添加国家或地区字段 2026-01-06 09:56:21 +08:00
501032ef17 TASK:试用用户试用期延长至7天 2025-12-31 13:15:13 +08:00
litianxiang
cb25bdd2e0 Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2025-12-29 14:57:41 +08:00
litianxiang
7a9fb0213b 适配新推荐接口 2025-12-29 13:49:31 +08:00
cd767dce6f BUGFIX:新用户不能获取历史系统通知 2025-12-24 11:47:11 +08:00
bf95b85841 to dev 2025-12-23 16:28:07 +08:00
9e58bd9e7d Merge branch 'release/3.1' into dev/3.1_release_merge
# Conflicts:
#	src/main/java/com/ai/da/common/utils/SendRequestUtil.java
2025-12-23 16:16:30 +08:00
d0ec5c5c26 BUGFIX:邮件发送失败 2025-12-23 16:03:48 +08:00
ab8aa5ea5c Merge branch 'dev/dev_xp' into dev/3.1_release_merge 2025-12-23 14:07:53 +08:00
aefcd2fdb0 BUGFIX:邮件发送失败 2025-12-23 14:07:26 +08:00
e74eab1070 BUGFIX:邮件发送失败 2025-12-23 14:05:20 +08:00
litianxiang
34da437a26 Merge remote-tracking branch 'origin/dev-ltx' into dev/3.1_release_merge 2025-12-22 11:36:23 +08:00
litianxiang
f84935d0bd 登录token存入redis 2025-12-22 11:35:38 +08:00
35edaa0f27 CONFIG: 拦截器配置 2025-12-19 21:43:02 +08:00
f43099e19e CONFIG: redis 配置修改 2025-12-19 21:21:21 +08:00
8079877734 CONFIG: TO DEV 2025-12-19 17:40:37 +08:00
ef686e38ac CONFIG: TO PROD 2025-12-19 17:33:51 +08:00
100019d2a4 Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev/3.1_release_merge 2025-12-19 16:00:55 +08:00
litianxiang
12af237d76 Merge remote-tracking branch 'origin/dev/3.1_release_merge' into dev/3.1_release_merge 2025-12-19 15:47:36 +08:00
litianxiang
44dbbb2a4b 更换Moodboard和Printboard提示词 2025-12-19 15:47:07 +08:00
9f42e153a4 Merge branch 'release/3.1' into dev/3.1_release_merge
# Conflicts:
#	.gitea/workflows/develop_build_manual.yaml
2025-12-19 15:01:32 +08:00
4fa70a1c90 BUGFIX:订阅计划创建不允许指定子账号为管理员 2025-12-18 18:03:03 +08:00
dfb34916e7 BUGFIX:订阅计划与子账号新增 2025-12-18 14:52:12 +08:00
9f7987306c BUGFIX:订阅计划 2025-12-18 13:38:38 +08:00
litianxiang
d3fc70fbf2 fix:edit产品图照成排序问题,恢复原逻辑 2025-12-17 16:08:49 +08:00
litianxiang
17645d17e5 关闭batch MQ监听 2025-12-17 16:02:49 +08:00
litianxiang
258eea5277 edit 产品图失败会导致sort不对试验5 2025-12-17 15:43:26 +08:00
litianxiang
bb1d3bd359 Revert "edit 产品图失败会导致sort不对试验5"
This reverts commit 6a8c87ed95.
2025-12-17 15:41:02 +08:00
litianxiang
6a8c87ed95 edit 产品图失败会导致sort不对试验5 2025-12-17 15:40:47 +08:00
6cd42b799a 删除 .gitea/workflows/prod_build_schedule.yaml 2025-12-01 10:22:29 +08:00
6e1ed7f9b8 删除 .gitea/workflows/prod_build_manual.yaml 2025-12-01 10:22:25 +08:00
b7be16738b 删除 .gitea/workflows/develop_build_manual.yaml 2025-12-01 10:22:21 +08:00
6da5e91ec1 删除 .gitea/workflows/develop_build_commit.yaml 2025-12-01 10:22:17 +08:00
a710fdd432 删除 docker-compose.yml 2025-11-30 11:01:12 +08:00
d598f53d3c Merge branch 'dev/3.1_release_merge' into release/3.1
# Conflicts:
#	.gitea/workflows/prod_build_schedule.yaml
2025-11-28 17:16:55 +08:00
96170a9956 更新 .gitea/workflows/prod_build_manual.yaml 2025-11-28 15:25:50 +08:00
8205fb5290 上传文件至「.gitea/workflows」 2025-11-28 15:25:41 +08:00
fcbe4762b3 TO prod 2025-11-27 17:38:24 +08:00
e750adcc94 TO prod 2025-11-27 17:35:47 +08:00
89 changed files with 8895 additions and 5485 deletions

View File

@@ -99,6 +99,8 @@ jobs:
volumes: volumes:
# 数据挂载 # 数据挂载
- ./log:/log - ./log:/log
- ./temp:/temp
- ./uploads:/temp/uploads
ports: ports:
- '10090:5567' - '10090:5567'
restart: always restart: always

View File

@@ -427,6 +427,11 @@
<artifactId>bcpkix-jdk18on</artifactId> <artifactId>bcpkix-jdk18on</artifactId>
<version>1.78.1</version> <version>1.78.1</version>
</dependency> </dependency>
<!-- AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -636,26 +636,26 @@ public class GenerateConsumer {
public void getPoseTransformationResult(Message msg, Channel channel) { public void getPoseTransformationResult(Message msg, Channel channel) {
processPoseTransformResult(msg, channel); processPoseTransformResult(msg, channel);
} }
@RabbitListener(queues = "#{rabbitMQProperties.queues.designBatch}") // @RabbitListener(queues = "#{rabbitMQProperties.queues.designBatch}")
@RabbitHandler // @RabbitHandler
public void getDesignBatchResult(Message msg, Channel channel) { // public void getDesignBatchResult(Message msg, Channel channel) {
processDesignBatchResult(msg, channel); // processDesignBatchResult(msg, channel);
} // }
@RabbitListener(queues = "#{rabbitMQProperties.queues.toProductImageBatch}") // @RabbitListener(queues = "#{rabbitMQProperties.queues.toProductImageBatch}")
@RabbitHandler // @RabbitHandler
public void getToProductImageBatchResult(Message msg, Channel channel) { // public void getToProductImageBatchResult(Message msg, Channel channel) {
processToProductImageBatchResult(msg, channel); // processToProductImageBatchResult(msg, channel);
} // }
//
@RabbitListener(queues = "#{rabbitMQProperties.queues.relightBatch}") // @RabbitListener(queues = "#{rabbitMQProperties.queues.relightBatch}")
@RabbitHandler // @RabbitHandler
public void getRelightBatchResult(Message msg, Channel channel) { // public void getRelightBatchResult(Message msg, Channel channel) {
processRelightBatchResult(msg, channel); // processRelightBatchResult(msg, channel);
} // }
//
@RabbitListener(queues = "#{rabbitMQProperties.queues.poseTransformBatch}") // @RabbitListener(queues = "#{rabbitMQProperties.queues.poseTransformBatch}")
@RabbitHandler // @RabbitHandler
public void getPoseTransformBatchResult(Message msg, Channel channel) { // public void getPoseTransformBatchResult(Message msg, Channel channel) {
processPoseTransformBatchResult(msg, channel); // processPoseTransformBatchResult(msg, channel);
} // }
} }

View File

@@ -28,6 +28,11 @@ public class MQPublisher {
amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getSr(), mm); amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getSr(), mm);
} }
public void sendGenerateResultMessage(String mm) {
log.info("send generate result message: {}", mm);
amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getGenerateResult(), mm);
}
/** /**
* *
* @param mailParams 含有的字段 * @param mailParams 含有的字段

View File

@@ -0,0 +1,170 @@
package com.ai.da.common.aspect;
import com.ai.da.common.context.UserContext;
import com.ai.da.model.vo.AuthPrincipalVo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.Map;
/**
* Controller日志切面
* 记录所有Controller接口的请求参数和用户信息
*/
@Aspect
@Component
public class ControllerLoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(ControllerLoggingAspect.class);
/**
* 定义切点所有Controller方法
*/
@Pointcut("execution(* com.ai.da.controller..*(..))")
public void controllerMethods() {
}
/**
* Controller方法执行前记录日志
*/
// @Before("controllerMethods()")
public void logControllerBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
// 获取当前用户ID
Long userId = null;
AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
if (authPrincipalVo != null) {
userId = authPrincipalVo.getId();
}
// 获取请求参数
Map<String, Object> params = getRequestParams(joinPoint, request);
logger.info("=== 请求开始 ===");
logger.info("用户ID: {}", userId);
logger.info("请求URL: {}", request.getRequestURL().toString());
logger.info("请求方法: {}", request.getMethod());
logger.info("请求IP: {}", getClientIpAddress(request));
logger.info("调用方法: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName());
logger.info("请求参数: {}", params);
}
}
/**
* 获取请求参数
*/
private Map<String, Object> getRequestParams(JoinPoint joinPoint, HttpServletRequest request) {
Map<String, Object> params = new HashMap<>();
// 1. 获取Query String参数
String queryString = request.getQueryString();
if (queryString != null && !queryString.isEmpty()) {
params.put("queryString", queryString);
}
// 2. 获取方法参数(包含 @PathVariable, @RequestParam, @RequestBody 等)
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
Map<String, Object> methodParams = new HashMap<>();
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
// 过滤掉不可序列化的参数
if (arg != null) {
if (isIgnorable(arg)) {
// 对于可忽略的类型,记录类型名
methodParams.put("arg" + i, "[" + arg.getClass().getSimpleName() + "]");
} else {
try {
methodParams.put("arg" + i, arg);
} catch (Exception e) {
methodParams.put("arg" + i, arg.toString());
}
}
}
}
if (!methodParams.isEmpty()) {
params.put("methodParams", methodParams);
}
}
return params;
}
/**
* 判断是否需要过滤的参数类型
*/
private boolean isIgnorable(Object obj) {
return obj instanceof HttpServletRequest
|| obj instanceof HttpServletResponse
|| obj instanceof MultipartFile
|| obj instanceof MultipartFile[];
}
/**
* Controller方法抛出异常时记录日志
*/
@AfterThrowing(pointcut = "controllerMethods()", throwing = "exception")
public void logControllerAfterThrowing(JoinPoint joinPoint, Throwable exception) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
Long userId = null;
AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
if (authPrincipalVo != null) {
userId = authPrincipalVo.getId();
}
// 获取请求参数
Map<String, Object> params = new HashMap<>();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
params = getRequestParams(joinPoint, request);
}
logger.error("=== 请求异常 ===");
logger.error("用户ID: {}", userId);
logger.error("调用方法: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName());
logger.error("请求参数: {}", params);
logger.error("异常信息: ", exception);
logger.error("=== 异常结束 ===");
}
/**
* 获取客户端真实IP地址
*/
private String getClientIpAddress(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
return xForwardedFor.split(",")[0];
}
String xRealIp = request.getHeader("X-Real-IP");
if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
return xRealIp;
}
String proxyClientIp = request.getHeader("Proxy-Client-IP");
if (proxyClientIp != null && !proxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(proxyClientIp)) {
return proxyClientIp;
}
String wlProxyClientIp = request.getHeader("WL-Proxy-Client-IP");
if (wlProxyClientIp != null && !wlProxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(wlProxyClientIp)) {
return wlProxyClientIp;
}
return request.getRemoteAddr();
}
}

View File

@@ -23,6 +23,7 @@ public class CommonConstant {
} }
public static final String GENERATE_PATH = "/api/generate_image"; public static final String GENERATE_PATH = "/api/generate_image";
public static final String GENERATE_PATH_FLUX2_KLEIN = "/api/generate_image_flux2_klein";
public static final String GENERATE_SINGLE_LOGO = "/api/generate_single_logo"; public static final String GENERATE_SINGLE_LOGO = "/api/generate_single_logo";

View File

@@ -20,7 +20,7 @@ public class ModelConstants {
public static final String PRINTBOARD_ADVANCED_T2I = "qwen-image"; public static final String PRINTBOARD_ADVANCED_T2I = "qwen-image";
public static final String MOODBOARD_ADVANCED = "doubao-seedream-3-0-t2i-250415"; public static final String MOODBOARD_ADVANCED = "doubao-seedream-3-0-t2i-250415";
public static final String PRINTBOARD_HIGH_T2I = "doubao-seedream-3-0-t2i-250415"; public static final String PRINTBOARD_HIGH_T2I = "doubao-seedream-3-0-t2i-250415";
public static final String PRINTBOARD_HIGH_I2I = "doubao-seededit-3-0-i2i-250628"; public static final String PRINTBOARD_HIGH_I2I = "doubao-seedream-4-0-250828";
public static final String PRINTBOARD_ADVANCED_I2I = "doubao-seedream-4-0-250828"; public static final String PRINTBOARD_ADVANCED_I2I = "doubao-seedream-4-0-250828";
public static final String IMAGEN_MODEL = "imagen-4.0-generate-001"; public static final String IMAGEN_MODEL = "imagen-4.0-generate-001";
public static final String NANO_BANANA = "gemini-2.5-flash-image"; public static final String NANO_BANANA = "gemini-2.5-flash-image";

View File

@@ -33,7 +33,11 @@ public enum AuthenticationOperationTypeEnum {
*/ */
UPDATE_USERINFO, UPDATE_USERINFO,
REGISTER; REGISTER,
/**
* Global_Award 活动验证
*/
GLOBAL_AWARD;
public static AuthenticationOperationTypeEnum of(String name) { public static AuthenticationOperationTypeEnum of(String name) {
return Stream.of(AuthenticationOperationTypeEnum.values()).filter(v -> v.name().equals(name)).findFirst().orElse(null); return Stream.of(AuthenticationOperationTypeEnum.values()).filter(v -> v.name().equals(name)).findFirst().orElse(null);

View File

@@ -6,6 +6,7 @@ import com.ai.da.common.context.UserContext;
import com.ai.da.common.security.config.SecurityProperties; import com.ai.da.common.security.config.SecurityProperties;
import com.ai.da.common.security.jwt.JWTTokenHelper; import com.ai.da.common.security.jwt.JWTTokenHelper;
import com.ai.da.common.utils.LocalCacheUtils; import com.ai.da.common.utils.LocalCacheUtils;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.utils.MultiReadHttpServletRequest; import com.ai.da.common.utils.MultiReadHttpServletRequest;
import com.ai.da.common.utils.MultiReadHttpServletResponse; import com.ai.da.common.utils.MultiReadHttpServletResponse;
import com.ai.da.common.utils.RequestInfoUtil; import com.ai.da.common.utils.RequestInfoUtil;
@@ -40,6 +41,8 @@ public class AuthenticationFilter extends OncePerRequestFilter {
private JWTTokenHelper jwtTokenHelper; private JWTTokenHelper jwtTokenHelper;
@Resource @Resource
private SecurityProperties properties; private SecurityProperties properties;
@Resource
private RedisUtil redisUtil;
private static final List<String> FILTER_URL = private static final List<String> FILTER_URL =
Arrays.asList("/favicon.ico", "/doc.html", "/swagger-ui.html", Arrays.asList("/favicon.ico", "/doc.html", "/swagger-ui.html",
@@ -56,7 +59,12 @@ public class AuthenticationFilter extends OncePerRequestFilter {
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify", "/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" "/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/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","/api/global-award/contestants/export",
"/api/global-award/contestants/export/files"
); );
@Override @Override
@@ -132,12 +140,19 @@ public class AuthenticationFilter extends OncePerRequestFilter {
UserContext.delete(); UserContext.delete();
//存取用户信息到缓存 //存取用户信息到缓存
UserContext.setUserHolder(principal); UserContext.setUserHolder(principal);
//校验token // 校验 token:先查本地缓存,再查 Redis保证服务重启后仍然有效
String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId())); String userIdStr = String.valueOf(principal.getId());
String cacheToken = LocalCacheUtils.getTokenCache(userIdStr);
if(StringUtils.isEmpty(cacheToken)){ if (StringUtils.isEmpty(cacheToken)) {
// 本地缓存为空时,尝试从 Redis 读取
cacheToken = redisUtil.getLoginToken(principal.getId());
if (StringUtils.isEmpty(cacheToken)) {
// throw new RuntimeException("TOKEN已过期请重新登录"); // throw new RuntimeException("TOKEN已过期请重新登录");
throw new TokenMissingOrExpiredException("TOKEN已过期请重新登录(local cache empty)"); throw new TokenMissingOrExpiredException("TOKEN已过期请重新登录(cache & redis empty)");
}
// 将 Redis 中的 token 回填到本地缓存,减少后续 Redis 访问
LocalCacheUtils.setTokenCache(userIdStr, cacheToken);
} }
if(!cacheToken.equals(jwtToken) ){ if(!cacheToken.equals(jwtToken) ){
// throw new RuntimeException("TOKEN已过期请重新登录"); // throw new RuntimeException("TOKEN已过期请重新登录");

View File

@@ -40,8 +40,8 @@ public class AccountTask {
accountService.extendValidityForCC(); accountService.extendValidityForCC();
} }
// 每天凌晨0点执行一次 // 每天凌晨0点执行一次 目前已没有角色类型为4的用户
@Scheduled(cron = "0 0 0 * * ?") /*@Scheduled(cron = "0 0 0 * * ?")
public void cancelActivityBenefits() { public void cancelActivityBenefits() {
// 1、查询当前所有参与了活动且过期的用户 // 1、查询当前所有参与了活动且过期的用户
List<Account> accountList = accountService.getExpiredUserBySystemUser(4); List<Account> accountList = accountService.getExpiredUserBySystemUser(4);
@@ -51,7 +51,7 @@ public class AccountTask {
log.info("参与活动的用户{} : {} 于 {} 账号有效期到期,置为游客", account.getId(), account.getUserEmail(), account.getValidEndTime()); log.info("参与活动的用户{} : {} 于 {} 账号有效期到期,置为游客", account.getId(), account.getUserEmail(), account.getValidEndTime());
accountService.toVisitor(account); accountService.toVisitor(account);
} }
} }*/
// 每天检测正式用户到期情况每天凌晨0点执行 // 每天检测正式用户到期情况每天凌晨0点执行
@Scheduled(cron = "0 0 0 * * ?") @Scheduled(cron = "0 0 0 * * ?")
@@ -92,7 +92,7 @@ public class AccountTask {
@Scheduled(cron = "0 5 0 * * ?") @Scheduled(cron = "0 5 0 * * ?")
public void activeSubscriptionPlan() { public void activeSubscriptionPlan() {
subscriptionPlanService.activeSubscriptionPlan(); subscriptionPlanService.activeSubscriptionPlan(null);
} }
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes

View File

@@ -97,7 +97,7 @@ public class PaymentTask {
// //
} }
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes // @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
public void updateAffiliateInfoWithPayment(){ public void updateAffiliateInfoWithPayment(){
// log.info("佣金计算定时器"); // log.info("佣金计算定时器");
affiliateService.updateAffiliateInfoWithPayment(); affiliateService.updateAffiliateInfoWithPayment();

View File

@@ -40,7 +40,7 @@ public class SubscriptionReminderTask {
REMINDER_DAYS_CONFIG.put("year", 14); REMINDER_DAYS_CONFIG.put("year", 14);
} }
@Scheduled(cron = "0 0 9 * * ?") // @Scheduled(cron = "0 0 9 * * ?")
public void subscriptionReminder() { public void subscriptionReminder() {
// 获取所有需要通知的订阅 // 获取所有需要通知的订阅
List<SubscriptionInfo> subscriptionInfos = getDueSubscriptions(); List<SubscriptionInfo> subscriptionInfos = getDueSubscriptions();
@@ -97,7 +97,7 @@ public class SubscriptionReminderTask {
return subscriptionInfoMapper.selectList(qw); return subscriptionInfoMapper.selectList(qw);
} }
@Scheduled(cron = "0 0 9 * * ?") // @Scheduled(cron = "0 0 9 * * ?")
public void trialReminder() { public void trialReminder() {
// 今天的 00:00:00 和 23:59:59 // 今天的 00:00:00 和 23:59:59
LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay(); LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay();

View File

@@ -41,6 +41,13 @@ public class MinioUtil {
@Autowired @Autowired
private MinioClient minioClient; private MinioClient minioClient;
/**
* 获取MinIO客户端实例
*/
public MinioClient getMinioClient() {
return minioClient;
}
/** /**
* description: 判断bucket是否存在不存在则创建 * description: 判断bucket是否存在不存在则创建
* *

View File

@@ -34,6 +34,11 @@ public class RedisUtil {
private RedisTemplate<String, String> redisTemplate; private RedisTemplate<String, String> redisTemplate;
public final static String FLUX_POLLING_URL = "Flux:"; public final static String FLUX_POLLING_URL = "Flux:";
/**
* 登录 token 在 Redis 中的前缀:
* 最终 key 结构为 login:token:{userId}
*/
public final static String LOGIN_TOKEN_KEY = "login:token:";
public Boolean hasKey(String key){ public Boolean hasKey(String key){
return redisTemplate.hasKey(key); return redisTemplate.hasKey(key);
@@ -186,6 +191,40 @@ public class RedisUtil {
redisTemplate.delete(key); redisTemplate.delete(key);
} }
/**
* 保存登录 token
*
* @param userId 用户 ID
* @param token token 字符串
* @param expireMillis 过期时间(毫秒,通常与 JWT 保持一致)
*/
public void setLoginToken(Long userId, String token, long expireMillis) {
if (expireMillis <= 0) {
// 不设置过期时间,直到手动删除(不推荐)
addToString(LOGIN_TOKEN_KEY + userId, token);
return;
}
long expireSeconds = expireMillis / 1000;
if (expireSeconds <= 0) {
expireSeconds = 1;
}
addToString(LOGIN_TOKEN_KEY + userId, token, expireSeconds);
}
/**
* 获取登录 token
*/
public String getLoginToken(Long userId) {
return getFromString(LOGIN_TOKEN_KEY + userId);
}
/**
* 删除登录 token
*/
public void deleteLoginToken(Long userId) {
removeFromString(LOGIN_TOKEN_KEY + userId);
}
public final static String PORTFOLIO_LIKE_KEY = "portfolio:like:"; public final static String PORTFOLIO_LIKE_KEY = "portfolio:like:";
public void likePost(Long portfolioId, Long userId) { public void likePost(Long portfolioId, Long userId) {

View File

@@ -1024,7 +1024,7 @@ public class SendEmailUtil {
log.info("邮件发送结果res###{}", SendEmailResponse.toJsonString(resp)); log.info("邮件发送结果res###{}", SendEmailResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) { } catch (TencentCloudSDKException e) {
log.info("邮件发送失败###{}", e.toString()); log.info("邮件发送失败###{}", e.toString());
throw new BusinessException("failed.to.send.mail"); // throw new BusinessException("failed.to.send.mail");
} }
} }

View File

@@ -114,8 +114,8 @@ public class SendRequestUtil {
public String sendFluxPost(String url, String requestBodyStr) { public String sendFluxPost(String url, String requestBodyStr) {
// 尝试两个API key // 尝试两个API key
String[] apiKeys = {"d447a0ac-2291-4f1c-9a36-f7614c385989", String[] apiKeys = {"84e8f5d5-b0b3-49aa-b244-ab7ba27e7ae7",
"84e8f5d5-b0b3-49aa-b244-ab7ba27e7ae7"}; "d447a0ac-2291-4f1c-9a36-f7614c385989"};
boolean[] notified = {false, false}; // 记录是否已发送过不足提醒 boolean[] notified = {false, false}; // 记录是否已发送过不足提醒
for (int i = 0; i < apiKeys.length; i++) { for (int i = 0; i < apiKeys.length; i++) {
@@ -140,8 +140,8 @@ public class SendRequestUtil {
if (status == 402 || status == 403) { if (status == 402 || status == 403) {
if (!notified[i]) { if (!notified[i]) {
SendEmailUtil.commonExceptionReminder( SendEmailUtil.commonExceptionReminder(
"Flux账户积分不足flux生成任务失败", "Flux账户积分不足flux生成任务失败key:" + apiKeys[i],
new String[]{"xupei3360@163.com, fangjianliao@aidlab.hk, investigation@aidlab.hk"} new String[]{"xupei3360@163.com", "fangjianliao@aidlab.hk", "investigation@aidlab.hk"}
); );
notified[i] = true; notified[i] = true;
} }

View File

@@ -15,16 +15,16 @@ import com.ai.da.model.vo.PersonalHomepageVO;
import com.ai.da.service.AccountService; import com.ai.da.service.AccountService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; 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 lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; 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.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -139,21 +139,21 @@ public class AccountController {
@Operation(summary = "aws状态检测") @Operation(summary = "aws状态检测")
@GetMapping("/healthy") @GetMapping("/healthy")
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
public Response<Map<String,Integer>> checkStatus(){ public Response<Map<String, Integer>> checkStatus() {
Map<String,Integer> returnMap = new HashMap<>(); Map<String, Integer> returnMap = new HashMap<>();
returnMap.put("code",200); returnMap.put("code", 200);
return Response.success(returnMap); return Response.success(returnMap);
} }
@Operation(summary = "查询账号到期时间") @Operation(summary = "查询账号到期时间")
@PostMapping("/getExpiredTime") @PostMapping("/getExpiredTime")
public Response<Long> getExpiredTime(){ public Response<Long> getExpiredTime() {
return Response.success(accountService.getExpiredTime()); return Response.success(accountService.getExpiredTime());
} }
@Operation(summary = "免密登录") @Operation(summary = "免密登录")
@PostMapping("/noLoginRequired") @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)); return Response.success(accountService.noLoginRequired(noLoginRequiredDTO, request));
} }
@@ -191,6 +191,7 @@ public class AccountController {
/** /**
* 参与活动 获取福利 * 参与活动 获取福利
*
* @return * @return
*/ */
/* @Operation(summary = "参与活动 获取福利") /* @Operation(summary = "参与活动 获取福利")
@@ -201,7 +202,7 @@ public class AccountController {
@Operation(summary = "将用户账号过期时间设置为过期当天的235959") @Operation(summary = "将用户账号过期时间设置为过期当天的235959")
@GetMapping("/setUserValidToDayEnd") @GetMapping("/setUserValidToDayEnd")
public Response<List<Long>> setUserValidToDayEnd(){ public Response<List<Long>> setUserValidToDayEnd() {
return Response.success(accountService.setUserValidToDayEnd()); return Response.success(accountService.setUserValidToDayEnd());
} }
@@ -223,19 +224,19 @@ public class AccountController {
@Operation(summary = "获取个人主页信息") @Operation(summary = "获取个人主页信息")
@GetMapping("/personalHomepage") @GetMapping("/personalHomepage")
public Response<PersonalHomepageVO> getPersonalHomepage(@RequestParam("id") Long id){ public Response<PersonalHomepageVO> getPersonalHomepage(@RequestParam("id") Long id) {
return Response.success(accountService.getPersonalHomepage(id)); return Response.success(accountService.getPersonalHomepage(id));
} }
@Operation(summary = "getUsernameModifyTimes") @Operation(summary = "getUsernameModifyTimes")
@GetMapping("/getNicknameModifyTimes") @GetMapping("/getNicknameModifyTimes")
public Response<Long> getNicknameModifyTimes(){ public Response<Long> getNicknameModifyTimes() {
return Response.success(accountService.getNicknameModifyTimes()); return Response.success(accountService.getNicknameModifyTimes());
} }
@Operation(summary = "editUserName") @Operation(summary = "editUserName")
@GetMapping("/editUserName") @GetMapping("/editUserName")
public Response<String> editUserName(@RequestParam("newUserName") String newUserName){ public Response<String> editUserName(@RequestParam("newUserName") String newUserName) {
accountService.editUserName(newUserName); accountService.editUserName(newUserName);
return Response.success("success"); return Response.success("success");
} }

View File

@@ -103,7 +103,7 @@ public class ConvenientInquiryController {
@GetMapping("/recentNewUserChart") @GetMapping("/recentNewUserChart")
public Response<Map<String, Object>> recentNewUserChart(@Parameter(description = "startTime") @RequestParam @Nullable String startTime, public Response<Map<String, Object>> recentNewUserChart(@Parameter(description = "startTime") @RequestParam @Nullable String startTime,
@Parameter(description = "endTime") @RequestParam @Nullable String endTime, @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)); return Response.success(convenientInquiryService.recentNewUserChart(startTime, endTime, userType));
} }
@@ -179,8 +179,10 @@ public class ConvenientInquiryController {
@Operation(summary = "获取所有用户id") @Operation(summary = "获取所有用户id")
@GetMapping("/getAllUserId") @GetMapping("/getAllUserId")
public Response<List<Map<String, Object>>> getAllUsrIdList() { public Response<IPage<Map<String, Object>>> getAllUserIdList(@Parameter(description = "page") @RequestParam Integer page,
return Response.success(convenientInquiryService.getAllUserIdList()); @Parameter(description = "size") @RequestParam Integer size,
@Parameter(description = "email 模糊查询") @RequestParam(required = false) String email) {
return Response.success(convenientInquiryService.getAllUserIdList(page, size, email));
} }
@Operation(summary = "获取所有交易信息") @Operation(summary = "获取所有交易信息")

View File

@@ -0,0 +1,187 @@
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 jakarta.servlet.http.HttpServletResponse;
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));
}
@GetMapping("/contestants/export")
@ApiOperation(value = "导出参赛者列表为Excel", notes = "导出所有参赛者信息为xlsx并触发下载")
public void exportContestants(HttpServletResponse response) throws Exception {
byte[] data = globalAwardService.exportContestants();
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=\"contestants.xlsx\"");
response.setContentLength(data.length);
response.getOutputStream().write(data);
response.getOutputStream().flush();
}
@PostMapping("/contestants/export/files")
@ApiOperation(value = "导出参赛者文件到本地", notes = "根据参赛者编号范围导出PDF和视频文件到本地temp/uploads/contestants目录")
public Response<Integer> exportContestantFiles(@ApiParam(value = "参赛者文件导出请求", required = true) @RequestBody ContestantExportRequest request) throws Exception {
int exportedCount = globalAwardService.exportContestantFiles(request.getMinContestantNumber(), request.getMaxContestantNumber());
return Response.success(exportedCount);
}
}

View File

@@ -23,6 +23,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collections; import java.util.Collections;
@@ -119,4 +122,14 @@ public class PythonController {
return Response.success(superResolutionService.prepareForSR(superResolutionDTO)); 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);
}
} }

View File

@@ -82,7 +82,7 @@ public class SubscriptionPlanController {
@Operation(summary = "activeSubscriptionPlan") @Operation(summary = "activeSubscriptionPlan")
@GetMapping("/activeSubscriptionPlan") @GetMapping("/activeSubscriptionPlan")
public Response<String> activeSubscriptionPlan() { public Response<String> activeSubscriptionPlan() {
subscriptionPlanService.activeSubscriptionPlan(); subscriptionPlanService.activeSubscriptionPlan(null);
return Response.success(); return Response.success();
} }

View 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> {
}

View File

@@ -19,7 +19,7 @@ public interface DesignMapper extends CommonMapper<Design> {
Long insertDesign(Design design); Long insertDesign(Design design);
List<UserDesignStatisticDTO> getDesignStatistic(String startTime, String endTime, List<Long> ids, String email, List<UserDesignStatisticDTO> getDesignStatistic(String startTime, String endTime, List<Long> ids, String email,
String role, String organizationName); String role, String organizationName, boolean filterBySecond);
List<Design> selectDeleteList(); List<Design> selectDeleteList();
} }

View File

@@ -4,6 +4,7 @@ import com.ai.da.common.config.mybatis.plus.CommonMapper;
import com.ai.da.mapper.primary.entity.Notification; import com.ai.da.mapper.primary.entity.Notification;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -20,5 +21,5 @@ public interface NotificationMapper extends CommonMapper<Notification> {
void setPersonalNotificationAllRead(String type, Long receiverId, LocalDateTime time); void setPersonalNotificationAllRead(String type, Long receiverId, LocalDateTime time);
List<Long> getUnreadSysNotification(Long accountId); List<Long> getUnreadSysNotification(Long accountId, Date createTime);
} }

View File

@@ -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> {
}

View File

@@ -3,6 +3,8 @@ package com.ai.da.mapper.primary;
import com.ai.da.common.config.mybatis.plus.CommonMapper; import com.ai.da.common.config.mybatis.plus.CommonMapper;
import com.ai.da.mapper.primary.entity.WorkspaceRelStyle; import com.ai.da.mapper.primary.entity.WorkspaceRelStyle;
import java.util.List;
/** /**
* Mapper 接口 * Mapper 接口
* *
@@ -11,5 +13,11 @@ import com.ai.da.mapper.primary.entity.WorkspaceRelStyle;
*/ */
public interface WorkspaceRelStyleMapper extends CommonMapper<WorkspaceRelStyle> { public interface WorkspaceRelStyleMapper extends CommonMapper<WorkspaceRelStyle> {
/**
* 根据projectId查询workspaceRelStyles
* @param projectId 项目ID
* @return workspaceRelStyles列表
*/
List<WorkspaceRelStyle> selectByProjectId(Long projectId);
} }

View File

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.io.Serializable; 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; 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 Date updateDate;
private Integer isTrial; private Integer isTrial;
@@ -142,4 +143,26 @@ public class Account implements Serializable {
private String givenName; private String givenName;
private Long subscriptionPlanId; 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;
}
}
} }

View File

@@ -0,0 +1,79 @@
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("contestant_number")
private Integer contestantNumber;
@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("video_duration")
private Integer videoDuration;
@TableField("video_size")
private Long videoSize;
@TableField("pdf_size")
private Long pdfSize;
@TableField("created_at")
private LocalDateTime createdAt;
@TableField("updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -62,4 +62,9 @@ public class DesignItemDetailPrint {
* 更新时间 * 更新时间
*/ */
private LocalDateTime updateDate; private LocalDateTime updateDate;
/**
* 对象信息JSON格式
*/
private String object;
} }

View File

@@ -67,6 +67,11 @@ public class SubscriptionPlan extends BaseEntity{
*/ */
private String status; private String status;
/**
* 国家或地区
*/
private String countryOrRegion;
// 在类内部定义的枚举 // 在类内部定义的枚举
@Getter @Getter
public enum SubscriptionStatus { public enum SubscriptionStatus {

View File

@@ -90,6 +90,19 @@ public class TDesignPythonOutfitDetail implements Serializable {
*/ */
@Schema(description = "图层优先级") @Schema(description = "图层优先级")
private Integer priority; private Integer priority;
/**
* 镜像模式
*/
@Schema(description = "镜像模式")
private String transpose;
/**
* 旋转角度
*/
@Schema(description = "旋转角度")
private Double rotate;
/** /**
* 创建时间 * 创建时间
*/ */

View File

@@ -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;
}

View File

@@ -23,5 +23,8 @@ public class UserPreferenceLogTest implements Serializable {
private Long accountId; private Long accountId;
private String path; private String path;
private LocalDateTime dataTime; private LocalDateTime dataTime;
private String category;
private String style;
private Long sysFileId;
} }

View File

@@ -0,0 +1,74 @@
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;
@ApiModelProperty(value = "视频时长(秒)", required = false, example = "120")
private Integer videoDuration;
@ApiModelProperty(value = "视频大小(字节)", required = false, example = "10485760")
private Long videoSize;
@ApiModelProperty(value = "PDF 文件大小(字节)", required = false, example = "524288")
private Long pdfSize;
// /**
// * 是否确认覆盖已存在记录false 表示发现已有记录时仅返回 existingRecord不覆盖
// */
// @ApiModelProperty(value = "是否确认覆盖已存在记录", required = false, example = "false")
// private Boolean confirm = false;
@NotBlank
private String secureToken;
}

View File

@@ -0,0 +1,19 @@
package com.ai.da.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 参赛者文件导出请求DTO
*/
@Data
@ApiModel(value = "参赛者文件导出请求", description = "用于导出指定范围的参赛者文件")
public class ContestantExportRequest {
@ApiModelProperty(value = "最小参赛者编号", required = true, example = "10000")
private Integer minContestantNumber;
@ApiModelProperty(value = "最大参赛者编号", required = true, example = "10010")
private Integer maxContestantNumber;
}

View File

@@ -43,6 +43,10 @@ public class DesignSingleIncludeLayersDTO implements Serializable {
@Schema(description = "项目id") @Schema(description = "项目id")
private Long projectId; private Long projectId;
@NotBlank(message = "designType cannot be empty")
@Schema(description = "default -> 新增sketch || merge")
private String designType;
@Override @Override
public String toString() { public String toString() {
return "DesignSingleIncludeLayersDTO{" + return "DesignSingleIncludeLayersDTO{" +

View File

@@ -1,5 +1,6 @@
package com.ai.da.model.dto; package com.ai.da.model.dto;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import com.ai.da.mapper.primary.entity.Gradient; import com.ai.da.mapper.primary.entity.Gradient;
@@ -67,4 +68,18 @@ public class DesignSingleItemDTO implements Serializable {
private PartialDesignDTO partialDesign; 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;
} }

View File

@@ -0,0 +1,55 @@
package com.ai.da.model.dto;
import lombok.Builder;
import lombok.Data;
import java.util.List;
/**
* 图片处理请求体
*/
@Data
@Builder
public class ImageProcessRequest {
/**
* OSS桶名bucket_name
*/
private String bucket_name;
/**
* OSS对象名object_name
*/
private String object_name;
/**
* 输入图片路径列表input_image_paths
*/
private List<String> input_image_paths;
/**
* 图像宽度width
*/
private Integer width;
/**
* 图像高度height
*/
private Integer height;
/**
* 文本提示prompt
*/
private String prompt;
/**
* 推理步数steps
*/
private Integer steps;
/**
* 引导系数guidance
*/
private Double guidance;
}

View File

@@ -20,17 +20,17 @@ public class PartialDesignDTO implements Serializable {
@Schema(description = "图片的base64格式") @Schema(description = "图片的base64格式")
private String partialDesignBase64; private String partialDesignBase64;
@Schema(description = "图层信息") /* @Schema(description = "图层信息")
private List<String> layers; private List<String> layers;*/
public PartialDesignDTO(String partialDesignMinioPath) { public PartialDesignDTO(String partialDesignMinioPath) {
this.partialDesignMinioPath = partialDesignMinioPath; this.partialDesignMinioPath = partialDesignMinioPath;
} }
public PartialDesignDTO(String partialDesignMinioPath, List<String> layers) { /* public PartialDesignDTO(String partialDesignMinioPath, List<String> layers) {
this.partialDesignMinioPath = partialDesignMinioPath; this.partialDesignMinioPath = partialDesignMinioPath;
this.layers = layers; this.layers = layers;
} }*/
public PartialDesignDTO(String partialDesignMinioPath, String partialDesignPath) { public PartialDesignDTO(String partialDesignMinioPath, String partialDesignPath) {
this.partialDesignMinioPath = partialDesignMinioPath; this.partialDesignMinioPath = partialDesignMinioPath;

View File

@@ -48,4 +48,7 @@ public class SubscriptionPlanDTO {
@Schema(description = "订阅计划状态") @Schema(description = "订阅计划状态")
private String status; private String status;
@Schema(description = "国家或地区")
private String CountryOrRegion;
} }

View File

@@ -12,9 +12,12 @@ public class SubscriptionPlanPageQuery extends QueryPageByTimeDTO {
@Schema(description = "组织id") @Schema(description = "组织id")
private Long organizationId; private Long organizationId;
@Schema(description = "管理id") @Schema(description = "管理id")
private Long adminAccId; private Long adminAccId;
@Schema(description = "状态 PENDING||ACTIVE||EXPIRED") @Schema(description = "状态 PENDING||ACTIVE||EXPIRED")
private List<String> status; private List<String> status;
@Schema(description = "国家或地区")
private String countryOrRegion;
} }

View File

@@ -32,4 +32,7 @@ public class UpdateSubscriptionPlanDTO {
@Schema(description = "订阅重命名") @Schema(description = "订阅重命名")
private String name; private String name;
@Schema(description = "国家或地区")
private String countryOrRegion;
} }

View 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;
}

View 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;
}

View 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 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View File

@@ -62,11 +62,11 @@ public class DesignItemClothesDetailVO {
@Schema(description = "渐变色信息") @Schema(description = "渐变色信息")
private Gradient gradient; private Gradient gradient;
@Schema(description = "未分割的图层") /* @Schema(description = "未分割的图层")
private String undividedLayer; private String undividedLayer;
@Schema(description = "添加single印花的未分割的图层") @Schema(description = "添加single印花的未分割的图层")
private String undividedLayerWithSinglePrint; private String undividedLayerWithSinglePrint;*/
@Schema(description = "局部design") @Schema(description = "局部design")
private PartialDesignDTO partialDesign; private PartialDesignDTO partialDesign;

View File

@@ -59,4 +59,16 @@ public class DesignPythonOutfitVO {
* 图层优先级 从10开始优先级数字越大越靠近上层 * 图层优先级 从10开始优先级数字越大越靠近上层
*/ */
private Integer priority; private Integer priority;
/**
* 镜像模式
*/
@Schema(description = "镜像模式")
private int[] transpose;
/**
* 旋转角度
*/
@Schema(description = "旋转角度")
private Double rotate;
} }

View File

@@ -42,6 +42,8 @@ public class DesignSinglePrint implements Serializable {
@Schema(description = "印花优先级") @Schema(description = "印花优先级")
private Integer priority; private Integer priority;
private String object;
public DesignSinglePrint() { public DesignSinglePrint() {
} }

View File

@@ -45,4 +45,7 @@ public class SubscriptionPlanVO {
@Schema(description = "命名") @Schema(description = "命名")
private String name; private String name;
@Schema(description = "国家或地区")
private String countryOrRegion;
} }

View File

@@ -2,8 +2,10 @@ package com.ai.da.python;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.exceptions.ExceptionUtil;
import com.ai.da.common.RabbitMQ.RabbitMQProperties;
import com.ai.da.common.config.FileProperties; import com.ai.da.common.config.FileProperties;
import com.ai.da.common.config.exception.BusinessException; import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext; import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.*; import com.ai.da.common.enums.*;
import com.ai.da.common.utils.*; import com.ai.da.common.utils.*;
@@ -20,6 +22,7 @@ import com.ai.da.model.vo.*;
import com.ai.da.python.vo.*; import com.ai.da.python.vo.*;
import com.ai.da.service.DesignHistoryService; import com.ai.da.service.DesignHistoryService;
import com.ai.da.service.PythonTAllInfoService; import com.ai.da.service.PythonTAllInfoService;
import com.ai.da.service.RabbitMQService;
import com.ai.da.service.SysFileService; import com.ai.da.service.SysFileService;
import com.alibaba.fastjson.*; import com.alibaba.fastjson.*;
import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.SerializerFeature;
@@ -39,6 +42,7 @@ import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
@@ -68,6 +72,8 @@ public class PythonService {
private String accessPythonPort; private String accessPythonPort;
@Value("${minio.bucketName.gradient}") @Value("${minio.bucketName.gradient}")
private String gradientBucketName; private String gradientBucketName;
@Value("${minio.bucketName.users}")
private String userBucketName;
@Value("${access.python.generate_sr_port}") @Value("${access.python.generate_sr_port}")
private String srServicePort; private String srServicePort;
@@ -83,6 +89,12 @@ public class PythonService {
@Resource @Resource
private RedisUtil redisUtil; private RedisUtil redisUtil;
@Resource
private RabbitMQService rabbitMQService;
@Resource
private RabbitMQProperties rabbitMQProperties;
/** /**
* 生成打印的图片 二合一 (废弃于2024/01/02) * 生成打印的图片 二合一 (废弃于2024/01/02)
* *
@@ -279,7 +291,7 @@ public class PythonService {
if (isDuplicate) { if (isDuplicate) {
elementVO.setHasUseMd5List(beforeAssemblyHasUseMd5List); elementVO.setHasUseMd5List(beforeAssemblyHasUseMd5List);
i --; i--;
switch (designPrintPictureType) { switch (designPrintPictureType) {
case PIN: case PIN:
@@ -406,13 +418,14 @@ public class PythonService {
// if (CollectionUtil.isEmpty(pinData)) { // if (CollectionUtil.isEmpty(pinData)) {
// return 0; // return 0;
// } // }
//// long topNum = sketchBoardElements.stream()
//// .filter(skecth -> skecth.getHasPin() == 1 /// / long topNum = sketchBoardElements.stream()
//// && DesignPythonItem.OUTWEAR_DRESS_BLOUSE.contains(skecth.getLevel2Type())).count(); /// / .filter(skecth -> skecth.getHasPin() == 1
//// long bottomNum = sketchBoardElements.stream() /// / && DesignPythonItem.OUTWEAR_DRESS_BLOUSE.contains(skecth.getLevel2Type())).count();
//// .filter(skecth -> skecth.getHasPin() == 1 /// / long bottomNum = sketchBoardElements.stream()
//// && DesignPythonItem.SKIRT_TROUSERS.contains(skecth.getLevel2Type())).count(); /// / .filter(skecth -> skecth.getHasPin() == 1
//// int num = Arrays.asList(topNum, bottomNum).stream().max(Comparator.comparing(Long::valueOf)).get().intValue(); /// / && DesignPythonItem.SKIRT_TROUSERS.contains(skecth.getLevel2Type())).count();
/// / int num = Arrays.asList(topNum, bottomNum).stream().max(Comparator.comparing(Long::valueOf)).get().intValue();
// int num = pinData.size(); // int num = pinData.size();
// return Math.min(num, 8); // return Math.min(num, 8);
// } // }
@@ -560,12 +573,12 @@ public class PythonService {
return 0; return 0;
} else { } else {
long pinNum = printBoardElements.stream().filter(f -> f.getHasPin() == 1).count(); long pinNum = printBoardElements.stream().filter(f -> f.getHasPin() == 1).count();
if (designNum - pinNum < 0){ if (designNum - pinNum < 0) {
return RandomsUtil.randomSysFile(0L, (long) (pinNum/2 + 1)); return RandomsUtil.randomSysFile(0L, (long) (pinNum / 2 + 1));
} else if (designNum - pinNum < designNum/2) { } else if (designNum - pinNum < designNum / 2) {
return RandomsUtil.randomSysFile(0L, designNum - pinNum + 1); return RandomsUtil.randomSysFile(0L, designNum - pinNum + 1);
} else { } else {
return RandomsUtil.randomSysFile(0L, (long) (designNum/2 + 1)); return RandomsUtil.randomSysFile(0L, (long) (designNum / 2 + 1));
} }
} }
} }
@@ -680,8 +693,7 @@ public class PythonService {
// 其他所有情况,都回退到使用系统推荐 // 其他所有情况,都回退到使用系统推荐
String categoryParam = elementVO.getModelSex().toLowerCase() + "_" + styleCategory.toLowerCase(); String categoryParam = elementVO.getModelSex().toLowerCase() + "_" + styleCategory.toLowerCase();
List<String> recommentdUrlList = getSystemSketchByCategory(categoryParam, elementVO.getBrandId(), elementVO.getBrandScale(),elementVO.getStyle()); List<String> recommentdUrlList = getSystemSketchByCategory(categoryParam, elementVO.getBrandId(), elementVO.getBrandScale(), elementVO.getStyle());
if (CollectionUtils.isEmpty(recommentdUrlList)) { if (CollectionUtils.isEmpty(recommentdUrlList)) {
throw new BusinessException("failed.to.obtain.system.sketch.recommendation"); throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
} }
@@ -1039,7 +1051,7 @@ public class PythonService {
if (!CollectionUtils.isEmpty(recommentdUrlList)) { if (!CollectionUtils.isEmpty(recommentdUrlList)) {
String recommendSystemSketch = recommentdUrlList.get(0); String recommendSystemSketch = recommentdUrlList.get(0);
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO); return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
}else { } else {
throw new BusinessException("failed.to.obtain.system.sketch.recommendation"); throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
} }
// JSONObject attributeRecognition = getAttributeRecognitionBySameCategory(element, validateElementVO.getModelSex()); // JSONObject attributeRecognition = getAttributeRecognitionBySameCategory(element, validateElementVO.getModelSex());
@@ -1062,7 +1074,7 @@ public class PythonService {
if (!CollectionUtils.isEmpty(recommentdUrlList)) { if (!CollectionUtils.isEmpty(recommentdUrlList)) {
String recommendSystemSketch = recommentdUrlList.get(0); String recommendSystemSketch = recommentdUrlList.get(0);
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO); return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
}else { } else {
throw new BusinessException("failed.to.obtain.system.sketch.recommendation"); throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
} }
@@ -1098,7 +1110,7 @@ public class PythonService {
if (!CollectionUtils.isEmpty(recommentdUrlList)) { if (!CollectionUtils.isEmpty(recommentdUrlList)) {
String recommendSystemSketch = recommentdUrlList.get(0); String recommendSystemSketch = recommentdUrlList.get(0);
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO); return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
}else { } else {
throw new BusinessException("failed.to.obtain.system.sketch.recommendation"); throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
} }
// JSONObject attributeRecognition = getAttributeRecognitionBySameCategory(element, validateElementVO.getModelSex()); // JSONObject attributeRecognition = getAttributeRecognitionBySameCategory(element, validateElementVO.getModelSex());
@@ -1122,7 +1134,7 @@ public class PythonService {
if (!CollectionUtils.isEmpty(recommentdUrlList)) { if (!CollectionUtils.isEmpty(recommentdUrlList)) {
String recommendSystemSketch = recommentdUrlList.get(0); String recommendSystemSketch = recommentdUrlList.get(0);
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO); return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
}else { } else {
throw new BusinessException("failed.to.obtain.system.sketch.recommendation"); throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
} }
// String tableName = getTableName(validateElementVO.getModelSex(), category); // String tableName = getTableName(validateElementVO.getModelSex(), category);
@@ -2190,7 +2202,7 @@ public class PythonService {
basic.setScale_earrings(0.16); basic.setScale_earrings(0.16);
if (Objects.nonNull(designLibraryModelPointVO)) { if (Objects.nonNull(designLibraryModelPointVO)) {
basic.setBody_point_test(getMap(designLibraryModelPointVO)); basic.setBody_point_test(getMap(designLibraryModelPointVO));
}else { } else {
basic.setBody_point_test(getMap(designLibraryModelPoint)); basic.setBody_point_test(getMap(designLibraryModelPoint));
} }
return basic; return basic;
@@ -2789,7 +2801,7 @@ public class PythonService {
DesignPythonObject pythonObject = new DesignPythonObject(); DesignPythonObject pythonObject = new DesignPythonObject();
designPythonObjects.setProcess_id(designSingleDTO.getProcessId()); designPythonObjects.setProcess_id(designSingleDTO.getProcessId());
pythonObject.setItems(coverToDesignSinglePythonItem(designSingleDTO, designLibraryModelPoint, singleOverall)); pythonObject.setItems(coverToDesignSinglePythonItem(designSingleDTO, designLibraryModelPoint, singleOverall));
pythonObject.setBasic(coverToSingleBasic(singleOverall, switchCategory, designLibraryModelPoint, previewOrSubmit)); pythonObject.setBasic(coverToSingleBasic(singleOverall, switchCategory, designLibraryModelPoint, designSingleDTO.getDesignType()));
objects.add(pythonObject); objects.add(pythonObject);
return designPythonObjects; return designPythonObjects;
} }
@@ -2851,10 +2863,17 @@ public class PythonService {
gradientString = JSONObject.toJSONString(designSingleItem.getGradient()); gradientString = JSONObject.toJSONString(designSingleItem.getGradient());
} }
PrintToPython printToPython = resolveDesignSinglePrint(designSingleItem.getPrintObject().getPrints(), PrintToPython printToPython;
printToPython = resolveDesignSinglePrint(designSingleItem.getPrintObject().getPrints(),
designSingleItem.getPartialDesign().getPartialDesignMinioPath()); designSingleItem.getPartialDesign().getPartialDesignMinioPath());
resolveDesignElement(designSingleItem.getTrims(), printToPython); /*PrintToPython printToPython = resolveDesignSinglePrint(designSingleItem.getPrintObject().getPrints(),
log.info("组装参数【服装:{}的maskUrl: {}】",designSingleItem.getType(), designSingleItem.getMaskUrl()); designSingleItem.getPartialDesign().getPartialDesignMinioPath());*/
// resolveDesignElement(designSingleItem.getTrims(), printToPython);
log.info("组装参数【服装:{}的maskUrl: {}】", designSingleItem.getType(), designSingleItem.getMaskUrl());
String partialDesign = designSingleItem.getPartialDesign().getPartialDesignMinioPath();
String mergeImagePath = !StringUtil.isNullOrEmpty(partialDesign)
? partialDesign : designSingleItem.getPath();
response.add(new DesignPythonItem( response.add(new DesignPythonItem(
designSingleItem.getType(), designSingleItem.getType(),
designSingleItem.getPath(), designSingleItem.getPath(),
@@ -2869,12 +2888,15 @@ public class PythonService {
designSingleItem.getPriority(), designSingleItem.getPriority(),
minioPath, minioPath,
gradientString, gradientString,
designSingleItem.getMaskUrl() designSingleItem.getMaskUrl(),
designSingleItem.getTranspose(),
designSingleItem.getRotate(),
mergeImagePath
)); ));
}); });
if (singleOverall.equals("overall")){ if (singleOverall.equals("overall")) {
String bodyPath; String bodyPath;
if (Objects.nonNull(designLibraryModelPoint)) { if (Objects.nonNull(designLibraryModelPoint)) {
bodyPath = designLibraryModelPoint.getTemplateUrl(); bodyPath = designLibraryModelPoint.getTemplateUrl();
@@ -2890,12 +2912,12 @@ public class PythonService {
private PrintToPython resolveDesignSinglePrint(List<DesignSinglePrint> printObject, String partialDesign) { private PrintToPython resolveDesignSinglePrint(List<DesignSinglePrint> printObject, String partialDesign) {
PrintToPython printToPython = new PrintToPython(); PrintToPython printToPython = new PrintToPython();
DesignPythonItemPrint printSingle = new DesignPythonItemPrint(); // DesignPythonItemPrint printSingle = new DesignPythonItemPrint();
DesignPythonItemPrint printOverall = new DesignPythonItemPrint(); DesignPythonItemPrint printOverall = new DesignPythonItemPrint();
printToPython.setSingle(printSingle); // printToPython.setSingle(printSingle);
printToPython.setOverall(printOverall); printToPython.setOverall(printOverall);
printToPython.setPartial(StringUtil.isNullOrEmpty(partialDesign) ? null : partialDesign); printToPython.setPartial(StringUtil.isNullOrEmpty(partialDesign) ? null : partialDesign);
if (Objects.isNull(printObject) || printObject.isEmpty()){ if (Objects.isNull(printObject) || printObject.isEmpty()) {
return printToPython; return printToPython;
} }
@@ -2926,12 +2948,12 @@ public class PythonService {
Integer priority = p.getPriority(); Integer priority = p.getPriority();
setUriToMinioPath(p); setUriToMinioPath(p);
// todo 下标越界问题 // todo 下标越界问题
if (p.getIfSingle()){ if (p.getIfSingle()) {
locationS.set(priority - 1, p.getLocation()); locationS.set(priority - 1, p.getLocation());
scaleS.set(priority - 1, p.getScale()); scaleS.set(priority - 1, p.getScale());
angleS.set( priority - 1, p.getAngle()); angleS.set(priority - 1, p.getAngle());
pathsS.set(priority - 1, p.getMinIOPath()); pathsS.set(priority - 1, p.getMinIOPath());
}else { } else {
locationO.set(priority - 1, p.getLocation()); locationO.set(priority - 1, p.getLocation());
scaleO.set(priority - 1, p.getScale()); scaleO.set(priority - 1, p.getScale());
angleO.set(priority - 1, p.getAngle()); angleO.set(priority - 1, p.getAngle());
@@ -2939,14 +2961,14 @@ public class PythonService {
} }
// log.info("本次print打点locations###{}###fileVO{}", p.getLocation(), JSON.toJSONString(fileVO)); // log.info("本次print打点locations###{}###fileVO{}", p.getLocation(), JSON.toJSONString(fileVO));
}); });
locationS.removeAll(Collections.singleton(null)); /*locationS.removeAll(Collections.singleton(null));
scaleS.removeAll(Collections.singleton(null)); scaleS.removeAll(Collections.singleton(null));
angleS.removeAll(Collections.singleton(null)); angleS.removeAll(Collections.singleton(null));
pathsS.removeAll(Collections.singleton(null)); pathsS.removeAll(Collections.singleton(null));
printSingle.setLocation(locationS); printSingle.setLocation(locationS);
printSingle.setPrint_scale_list(scaleS); printSingle.setPrint_scale_list(scaleS);
printSingle.setPrint_angle_list(angleS); printSingle.setPrint_angle_list(angleS);
printSingle.setPrint_path_list(pathsS); printSingle.setPrint_path_list(pathsS);*/
locationO.removeAll(Collections.singleton(null)); locationO.removeAll(Collections.singleton(null));
scaleO.removeAll(Collections.singleton(null)); scaleO.removeAll(Collections.singleton(null));
@@ -2961,9 +2983,9 @@ public class PythonService {
} }
// 对印花类型为Generate的图片路径进行特殊处理 // 对印花类型为Generate的图片路径进行特殊处理
private void setUriToMinioPath(DesignSinglePrint print){ private void setUriToMinioPath(DesignSinglePrint print) {
if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Generate")){ if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Generate")) {
if (!StringUtil.isNullOrEmpty(print.getPath())){ if (!StringUtil.isNullOrEmpty(print.getPath())) {
try { try {
URI uri = new URI(print.getPath()); URI uri = new URI(print.getPath());
String path = uri.getPath(); // 获取路径部分: /aida-users/87/print/9ac32f65-6043-424d-a146-92c9c6d204ee-4-87.png String path = uri.getPath(); // 获取路径部分: /aida-users/87/print/9ac32f65-6043-424d-a146-92c9c6d204ee-4-87.png
@@ -3047,7 +3069,7 @@ public class PythonService {
*/ */
private DesignPythonBasic coverToSingleBasic(String singleOverall, String switchCategory, private DesignPythonBasic coverToSingleBasic(String singleOverall, String switchCategory,
DesignLibraryModelPointVO designLibraryModelPoint, DesignLibraryModelPointVO designLibraryModelPoint,
String previewOrSubmit) { String designType) {
DesignPythonBasic basic = new DesignPythonBasic(); DesignPythonBasic basic = new DesignPythonBasic();
basic.setSingle_overall(singleOverall); basic.setSingle_overall(singleOverall);
basic.setSwitch_category(switchCategory); basic.setSwitch_category(switchCategory);
@@ -3059,7 +3081,8 @@ public class PythonService {
basic.setScale_earrings(0.16); basic.setScale_earrings(0.16);
basic.setBody_point_test(getMap(designLibraryModelPoint)); basic.setBody_point_test(getMap(designLibraryModelPoint));
basic.setLayer_order(Boolean.TRUE); basic.setLayer_order(Boolean.TRUE);
basic.setPreview_submit(previewOrSubmit); // basic.setPreview_submit(previewOrSubmit);
basic.setDesign_type(designType);
return basic; return basic;
} }
@@ -3322,7 +3345,7 @@ public class PythonService {
throw new BusinessException("system error!"); throw new BusinessException("system error!");
} }
public Boolean generateSketchOrPrint(String params, String port, String servicePath) { public Boolean generateSketchOrPrint(String params, String port, String servicePath, String taskId) {
//限流校验 //限流校验
// AccessLimitUtils.validate("generateSketchOrPrint", 5); // AccessLimitUtils.validate("generateSketchOrPrint", 5);
OkHttpClient client = new OkHttpClient().newBuilder() OkHttpClient client = new OkHttpClient().newBuilder()
@@ -3384,12 +3407,36 @@ public class PythonService {
if (result && jsonObject.get("code").equals(200)) { if (result && jsonObject.get("code").equals(200)) {
log.info("Generate##responseObject###{}", jsonObject); log.info("Generate##responseObject###{}", jsonObject);
// return setGenerateImageList(jsonObject.getJSONObject("data")); // return setGenerateImageList(jsonObject.getJSONObject("data"));
if (servicePath == CommonConstant.GENERATE_PATH_FLUX2_KLEIN) {
//放入结果到mq
JSONObject data = jsonObject.getJSONObject("data");
String outputPath = data.getString("output_path");
Map<String, String> mqMessage = new HashMap<>();
mqMessage.put("tasks_id", taskId);
mqMessage.put("status", "SUCCESS");
mqMessage.put("message", "success");
mqMessage.put("image_url", outputPath);
mqMessage.put("category", "");
String mqMessageBody = JSON.toJSONString(mqMessage);
rabbitMQService.publishMessageToGenerateResult(mqMessageBody);
}
return Boolean.TRUE; return Boolean.TRUE;
} else { } else {
log.info("generateSketchOrPrintPrint失败###{}", jsonObject); log.info("generateSketchOrPrintPrint失败###{}", jsonObject);
log.info("Generate Exception! Code : " + jsonObject.get("code")); log.info("Generate Exception! Code : " + jsonObject.get("code"));
Map<String, String> mqMessage = new HashMap<>();
mqMessage.put("tasks_id", taskId);
mqMessage.put("status", "ERROR");
mqMessage.put("message", "");
mqMessage.put("image_url", "");
mqMessage.put("category", "");
String mqMessageBody = JSON.toJSONString(mqMessage);
rabbitMQService.publishMessageToGenerateResult(mqMessageBody);
return Boolean.FALSE; return Boolean.FALSE;
} }
} }
public Response sendPostToModel(String content, String portAndRoute, String functionName) { public Response sendPostToModel(String content, String portAndRoute, String functionName) {
@@ -3431,7 +3478,10 @@ public class PythonService {
return imageUrlList; return imageUrlList;
}*/ }*/
/** 废弃状态 */
/**
* 废弃状态
*/
public String composeLayers(List<OutfitDetailPythonItem> layersDetail) { public String composeLayers(List<OutfitDetailPythonItem> layersDetail) {
HashMap<String, List<OutfitDetailPythonItem>> layers = new HashMap<>(); HashMap<String, List<OutfitDetailPythonItem>> layers = new HashMap<>();
HashMap<String, HashMap<String, List<OutfitDetailPythonItem>>> content = new HashMap<>(); HashMap<String, HashMap<String, List<OutfitDetailPythonItem>>> content = new HashMap<>();
@@ -3742,7 +3792,7 @@ public class PythonService {
throw new BusinessException("relightImage.interface.exception"); throw new BusinessException("relightImage.interface.exception");
} }
public String imageToSketch(String imagePath, String bucket, String objectName, String styleCode, String styleImageUrl){ public String imageToSketch(String imagePath, String bucket, String objectName, String styleCode, String styleImageUrl) {
OkHttpClient client = new OkHttpClient().newBuilder() OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(30, TimeUnit.SECONDS) .connectTimeout(30, TimeUnit.SECONDS)
.pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒) .pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒)
@@ -3789,7 +3839,7 @@ public class PythonService {
String sketchResult = jsonObject.get("data").toString(); String sketchResult = jsonObject.get("data").toString();
log.info("ImageToSketch 结果 {}", sketchResult); log.info("ImageToSketch 结果 {}", sketchResult);
return sketchResult; return sketchResult;
}else { } else {
log.info("ImageToSketch 失败。 Response code {}", responseCode); log.info("ImageToSketch 失败。 Response code {}", responseCode);
throw new BusinessException("ImageToSketch 失败。 Response code " + responseCode); throw new BusinessException("ImageToSketch 失败。 Response code " + responseCode);
} }
@@ -3842,7 +3892,7 @@ public class PythonService {
throw new BusinessException("bright.interface.exception"); throw new BusinessException("bright.interface.exception");
} }
public JSONObject attributeRecognition(List<String> pictureUrls,List<String> ids, List<String> category) { public JSONObject attributeRecognition(List<String> pictureUrls, List<String> ids, List<String> category) {
//限流校验 //限流校验
AccessLimitUtils.validate("attributeRecognition", 20); AccessLimitUtils.validate("attributeRecognition", 20);
OkHttpClient client = new OkHttpClient().newBuilder() OkHttpClient client = new OkHttpClient().newBuilder()
@@ -3874,7 +3924,7 @@ public class PythonService {
} catch (IOException ioException) { } catch (IOException ioException) {
log.error("PythonService###attributeRecognition异常##{}", ExceptionUtil.getThrowableList(ioException)); log.error("PythonService###attributeRecognition异常##{}", ExceptionUtil.getThrowableList(ioException));
} }
log.info("识别python对应的属性标签值结果###{}",bodyStr.trim()); log.info("识别python对应的属性标签值结果###{}", bodyStr.trim());
//去除限流 //去除限流
AccessLimitUtils.validateOut("attributeRecognition"); AccessLimitUtils.validateOut("attributeRecognition");
if (Objects.isNull(response)) { if (Objects.isNull(response)) {
@@ -3958,30 +4008,38 @@ public class PythonService {
throw new BusinessException("design.interface.exception"); throw new BusinessException("design.interface.exception");
} }
public List<String> getSystemSketchByCategory(String category, Long brandId, Double brandScale,String style) { public List<String> getSystemSketchByCategory(String category, Long brandId, Double brandScale, String style) {
//******3.1.2版本临时使用java推荐方案去解决style未使用的问题********** //******3.1.2版本临时使用java推荐方案去解决style未使用的问题**********
try { // try {
//使用新库attribute_retrieval_style表命名修改为elementVO.getModelSex().toLowerCase() + "_" + styleCategory.toLowerCase()比如female_skirt,与传入的category保持一致 // //使用新库attribute_retrieval_style表命名修改为elementVO.getModelSex().toLowerCase() + "_" + styleCategory.toLowerCase()比如female_skirt,与传入的category保持一致
Integer countByStyle = attributeRetrievalMapper.getCountByStyle(category, style); // Integer countByStyle = attributeRetrievalMapper.getCountByStyle(category, style);
//根据数量随机获取一个系统sketch // //根据数量随机获取一个系统sketch
if (countByStyle > 0) { // if (countByStyle > 0) {
//获取一个不大于countByStyle的随机整数 // //获取一个不大于countByStyle的随机整数
Integer randomNum = RandomUtils.nextInt(0, countByStyle); // Integer randomNum = RandomUtils.nextInt(0, countByStyle);
//返回格式为 dress/0902000649.jpg // //返回格式为 dress/0902000649.jpg
String oneSystemSketchRadom = attributeRetrievalMapper.getOneSystemSketchRadom(category, style,randomNum); // String oneSystemSketchRadom = attributeRetrievalMapper.getOneSystemSketchRadom(category, style,randomNum);
String imgName = oneSystemSketchRadom.split("/")[1]; // String imgName = oneSystemSketchRadom.split("/")[1];
//补齐正确格式aida-sys-image/images/male/tops/mens_test_5689.png // //补齐正确格式aida-sys-image/images/male/tops/mens_test_5689.png
//裁切category前半部分 // //裁切category前半部分
String sex = category.split("_")[0]; // String sex = category.split("_")[0];
String realCategory = category.split("_")[1]; // String realCategory = category.split("_")[1];
String path = "aida-sys-image/images/" + sex + "/" +realCategory +"/" +imgName; // String path = "aida-sys-image/images/" + sex + "/" +realCategory +"/" +imgName;
return Arrays.asList(path); // return Arrays.asList(path);
} // }
} catch (Exception e) { // } catch (Exception e) {
log.info("推荐失败:{}",e.getMessage()); // log.info("推荐失败:{}",e.getMessage());
throw new BusinessException("system.error"); // throw new BusinessException("system.error");
} // }
//**********************end*********************************** //**********************end***********************************
//临时补丁
if (category != null && style != null) {
String categoryPrefix = category.split("_")[0];
if ("male".equals(categoryPrefix) &&
(style.equalsIgnoreCase("lolita") || style.equalsIgnoreCase("romantic"))) {
style = null;
}
}
AuthPrincipalVo userHolder = UserContext.getUserHolder(); AuthPrincipalVo userHolder = UserContext.getUserHolder();
OkHttpClient client = new OkHttpClient().newBuilder() OkHttpClient client = new OkHttpClient().newBuilder()
@@ -3999,13 +4057,19 @@ public class PythonService {
String brandIdParam = (brandId != null) ? "" + brandId : "-1"; String brandIdParam = (brandId != null) ? "" + brandId : "-1";
String brandScaleParam = (brandScale != null) ? String.format("%.2f", brandScale) : "0.00"; String brandScaleParam = (brandScale != null) ? String.format("%.2f", brandScale) : "0.00";
String url = String.format("%s:%s/api/recommend/%d/%s/1/%s/%s", String baseUrl = String.format("%s:%s/api/recommend/%d/%s",
"http://18.167.251.121", "http://18.167.251.121",
accessPythonPort, accessPythonPort,
userHolder.getId(), userHolder.getId(),
category, category);
brandIdParam,
brandScaleParam); // 如果style不为空则添加到查询参数中
String url;
if (style != null && !style.isEmpty()) {
url = baseUrl + "?style=" + style;
} else {
url = baseUrl;
}
log.info("Recommendation request URL: {}", url); log.info("Recommendation request URL: {}", url);
@@ -4097,6 +4161,82 @@ public class PythonService {
//生成失败 //生成失败
throw new BusinessException("segProduct.interface.exception"); throw new BusinessException("segProduct.interface.exception");
} }
/**
* 转发 seg_anything 请求到 python 服务
* 请求 body 由调用方组装(包含 user_id, image_path, type, points, labels, box 等)
*/
public String segAnything(JSONObject content) {
OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(30, TimeUnit.SECONDS)
.pingInterval(5, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();
MediaType mediaType = MediaType.parse("application/json");
content.put("bucket", userBucketName);
content.put("object_name", content.get("user_id") + "/" + "segment" + "/" + UUID.randomUUID() + ".png");
content.remove("user_id");
RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(content));
String url = accessPythonIp + ":" + accessPythonPort + "/api/seg_anything";
log.info("segAnything 请求地址: {} 参数:{}", url, JSON.toJSONString(content));
Request request = new Request.Builder()
.url(url)
.method("POST", body)
.addHeader("Content-Type", "application/json")
.build();
Response response;
try {
response = client.newCall(request).execute();
} catch (IOException ioException) {
log.error("PythonService##segAnything异常###{}", ExceptionUtil.getThrowableList(ioException));
throw new BusinessException("segAnything.interface.exception");
}
if (response.isSuccessful()) {
try {
if (Objects.nonNull(response.body())) {
String responseBody = response.body().string();
JSONObject responseObject = JSON.parseObject(responseBody);
log.info("PythonService##segAnything response###{}", responseObject);
// Extract nested output path and convert to presigned MinIO URL for frontend, return as String
try {
if (responseObject != null) {
JSONObject dataObj = responseObject.getJSONObject("data");
if (dataObj != null) {
String output = dataObj.getString("output");
if (!StringUtil.isNullOrEmpty(output)) {
try {
String presigned = minioUtil.getPreSignedUrl(output, 24 * 60);
return presigned;
} catch (Exception e) {
log.error("Failed to generate presigned url for {}: {}", output, e.getMessage());
throw new BusinessException("segAnything.presign.failed");
}
}
}
}
} catch (Exception ex) {
log.error("PythonService##segAnything post-process error###{}", ex.getMessage());
throw new BusinessException("segAnything.interface.exception");
}
throw new BusinessException("segAnything.missing.output");
}
throw new BusinessException("segAnything.interface.exception");
} catch (IOException | JSONException e) {
log.error("PythonService##segAnything异常###{}", e.getMessage());
throw new BusinessException("segAnything.interface.exception");
}
}
log.error("PythonService##segAnything异常response###{}", response);
throw new BusinessException("segAnything.interface.exception");
}
public Boolean poseTransformation(JSONObject content, String apiUri) { public Boolean poseTransformation(JSONObject content, String apiUri) {
OkHttpClient client = new OkHttpClient().newBuilder() OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(30, TimeUnit.SECONDS) .connectTimeout(30, TimeUnit.SECONDS)
@@ -4200,7 +4340,7 @@ public class PythonService {
String modifiedModel = jsonObject.get("data").toString(); String modifiedModel = jsonObject.get("data").toString();
log.info("modifyModelProportion 结果 {}", modifiedModel); log.info("modifyModelProportion 结果 {}", modifiedModel);
return modifiedModel; return modifiedModel;
}else { } else {
log.info("modifyModelProportion 失败。 Response code {}", responseCode); log.info("modifyModelProportion 失败。 Response code {}", responseCode);
throw new BusinessException("modifyModelProportion 失败。 Response code " + responseCode); throw new BusinessException("modifyModelProportion 失败。 Response code " + responseCode);
} }
@@ -4225,7 +4365,7 @@ public class PythonService {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("prompt", prompt); map.put("prompt", prompt);
AuthPrincipalVo userHolder = UserContext.getUserHolder(); AuthPrincipalVo userHolder = UserContext.getUserHolder();
map.put("user_id", userHolder.getId()); map.put("user_id", userHolder.getId().toString());
log.info("brandDNAGenerate请求python 参数:####{}", map); log.info("brandDNAGenerate请求python 参数:####{}", map);
String param = JSON.toJSONString(map, SerializerFeature.WriteNullStringAsEmpty); String param = JSON.toJSONString(map, SerializerFeature.WriteNullStringAsEmpty);
log.info(param); log.info(param);
@@ -4303,10 +4443,11 @@ public class PythonService {
log.info("imageSegmentation 结果 {}", jsonObject.get("data").toString()); log.info("imageSegmentation 结果 {}", jsonObject.get("data").toString());
List<ImageSegmentation.ImageDate> seg = JSONObject.parseObject( List<ImageSegmentation.ImageDate> seg = JSONObject.parseObject(
jsonObject.get("data").toString(), jsonObject.get("data").toString(),
new TypeReference<List<ImageSegmentation.ImageDate>>() {} new TypeReference<List<ImageSegmentation.ImageDate>>() {
}
); );
return seg; return seg;
}else { } else {
log.info("imageSegmentation 失败。 Response code {}", responseCode); log.info("imageSegmentation 失败。 Response code {}", responseCode);
throw new BusinessException("imageSegmentation 失败。 Response code " + responseCode); throw new BusinessException("imageSegmentation 失败。 Response code " + responseCode);
} }

View File

@@ -16,7 +16,7 @@ public class DesignPythonBasic {
private String single_overall; private String single_overall;
private String preview_submit; // private String preview_submit;
private String switch_category; private String switch_category;
/** /**
@@ -40,4 +40,7 @@ public class DesignPythonBasic {
private Boolean layer_order = Boolean.FALSE; private Boolean layer_order = Boolean.FALSE;
// default | merge
private String design_type = "default";
} }

View File

@@ -87,6 +87,22 @@ public class DesignPythonItem {
*/ */
private String seg_mask_url; 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(), public static List<String> OUTWEAR_DRESS_BLOUSE = Arrays.asList(CollectionLevel2TypeEnum.OUTWEAR.getRealName(),
CollectionLevel2TypeEnum.DRESS.getRealName(), CollectionLevel2TypeEnum.BLOUSE.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, 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, 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.type = type;
this.path = path; this.path = path;
this.color = color; this.color = color;
@@ -157,6 +174,9 @@ public class DesignPythonItem {
this.gradient = gradient; this.gradient = gradient;
this.gradientString = gradientString; this.gradientString = gradientString;
this.seg_mask_url = seg_mask_url; 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) { public DesignPythonItem(String type, String path, String color, PrintToPython print, String icon, Long businessId, Long image_id) {

View File

@@ -53,7 +53,7 @@ public class DesignPythonItemPrint {
if (ifDesign){ if (ifDesign){
this.print_path_list = print_path_list; this.print_path_list = print_path_list;
this.location = Collections.singletonList(Arrays.asList(0.0f, 0.0f)); this.location = Collections.singletonList(Arrays.asList(0.0f, 0.0f));
this.print_scale_list = Collections.singletonList(Arrays.asList(0.0f, 0.0f)); this.print_scale_list = Collections.singletonList(Arrays.asList(1.0f, 1.0f));
this.print_angle_list = Arrays.asList(0.0, 0.0); this.print_angle_list = Arrays.asList(0.0, 0.0);
} }

View File

@@ -246,4 +246,6 @@ public interface AccountService extends IService<Account> {
void setEduAdminToExpire(Account adminAccount); void setEduAdminToExpire(Account adminAccount);
String getOrganizationTypeByRole(Integer roleNum); String getOrganizationTypeByRole(Integer roleNum);
void validateUserValidaExpire(Account account);
} }

View File

@@ -51,7 +51,7 @@ public interface ConvenientInquiryService extends IService<Questionnaire> {
IPage<Account> getUserInfo(QueryUserConditionsVO queryUserConditionsVO); 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); PageBaseResponse<PaymentInfoVO> queryTransactionRecords(QueryPaymentInfoDTO queryPaymentInfoDTO);

View File

@@ -53,7 +53,7 @@ public interface DesignItemService extends IService<DesignItem> {
DesignSingleVO designSingleIncludeLayers(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO); 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); Map<String, String> setTypeAndUndividedLayer(JSONArray layers);

View File

@@ -0,0 +1,44 @@
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);
/**
* 导出参赛者列表为 Excel二进制
* @return Excel 文件的字节数组
*/
byte[] exportContestants() throws Exception;
/**
* 将参赛者列表导出并保存到服务端本地目录(使用服务配置的 uploadDir/exports
*/
void saveContestantsToLocal() throws Exception;
/**
* 根据参赛者编号范围导出参赛者文件到本地目录
* @param minContestantNumber 最小参赛者编号
* @param maxContestantNumber 最大参赛者编号
* @return 导出的参赛者数量
*/
int exportContestantFiles(Integer minContestantNumber, Integer maxContestantNumber) throws Exception;
}

View File

@@ -7,6 +7,8 @@ public interface RabbitMQService {
void publishMessageToGenerate(String message); void publishMessageToGenerate(String message);
void publishMessageToGenerateResult(String message);
void publishMessageToSR(String message); void publishMessageToSR(String message);
Integer getMessageCount(String queueUrl); Integer getMessageCount(String queueUrl);

View File

@@ -26,7 +26,7 @@ public interface SubscriptionPlanService extends IService<SubscriptionPlan> {
void switchSubAccSubscriptionPlan(Long subscriptionPlanId, Long subAccId); void switchSubAccSubscriptionPlan(Long subscriptionPlanId, Long subAccId);
void activeSubscriptionPlan(); void activeSubscriptionPlan(Long planId);
void expireSubscription(); void expireSubscription();
} }

View File

@@ -39,9 +39,10 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Value; 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.core.io.ClassPathResource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@@ -83,6 +84,9 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService { public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {
@Resource
private ApplicationContext applicationContext;
@Resource @Resource
private AccountMapper accountMapper; private AccountMapper accountMapper;
@@ -132,6 +136,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
@Resource @Resource
private RedisUtil redisUtil; private RedisUtil redisUtil;
@Resource
private com.ai.da.common.security.config.SecurityProperties securityProperties;
@Resource @Resource
private UserFollowService userFollowService; private UserFollowService userFollowService;
@@ -237,12 +244,13 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class); AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
response.setEmail(account.getUserEmail()); response.setEmail(account.getUserEmail());
String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId())); String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
if (StringUtils.isNotBlank(token)) { /*if (StringUtils.isNotBlank(token)) {
//用户已登入 //用户已登入
response.setToken(token); response.setToken(token);
} else { } else {
response.setToken(createAccountToken(account)); response.setToken(createAccountToken(account));
} }*/
response.setToken(createAccountToken(account));
response.setUserId(account.getId()); response.setUserId(account.getId());
response.setSystemUser(account.getSystemUser()); response.setSystemUser(account.getSystemUser());
// 设置头像 // 设置头像
@@ -276,7 +284,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
// 定义常量(临时) // 定义常量(临时)
private static final Integer SYSTEM_USER_TYPE_EDU_ADMIN = 7; 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(); Long currentTime = new Date().getTime();
if (account.getSystemUser().equals(0)) { if (account.getSystemUser().equals(0)) {
return; return;
@@ -294,7 +302,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
if (isEduAdmin) { if (isEduAdmin) {
setEduAdminToExpire(account); setEduAdminToExpire(account);
} else { } else {
toVisitor(account); // 这里调用代理的 toVisitor 方法
AccountService proxy = applicationContext.getBean(AccountService.class);
proxy.toVisitor(account);
// return; // return;
throw new BusinessException("account.expired", ResultEnum.PROMPT.getCode()); throw new BusinessException("account.expired", ResultEnum.PROMPT.getCode());
} }
@@ -344,7 +354,11 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
principal.setLanguage(account.getLanguage()); principal.setLanguage(account.getLanguage());
principal.setCountry(account.getCountry()); principal.setCountry(account.getCountry());
String token2 = jwtTokenHelper.createToken(principal); String token2 = jwtTokenHelper.createToken(principal);
// 本地 JVM 缓存(适配旧逻辑)
LocalCacheUtils.setTokenCache(String.valueOf(account.getId()), token2); LocalCacheUtils.setTokenCache(String.valueOf(account.getId()), token2);
// 同步写入 Redis重启后仍然可用
long jwtExpiration = securityProperties.getJwtExpiration();
redisUtil.setLoginToken(account.getId(), token2, jwtExpiration);
return token2; return token2;
} }
@@ -600,21 +614,25 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
@Override @Override
public Boolean logout(AccountLogoutDTO accountLogoutDTO) { public Boolean logout(AccountLogoutDTO accountLogoutDTO) {
//jwt本身失效比较难做 统一用缓存实现 删除缓存失效 // jwt 本身失效比较难做统一用缓存实现删除缓存失效
String token = LocalCacheUtils.getTokenCache(String.valueOf(accountLogoutDTO.getUserId())); String userIdStr = String.valueOf(accountLogoutDTO.getUserId());
if (StringUtils.isNotBlank(token)) { LocalCacheUtils.delTokenCache(userIdStr);
LocalCacheUtils.delTokenCache(String.valueOf(accountLogoutDTO.getUserId())); // 同时删除 Redis 中的 token防止服务重启后仍然有效
} redisUtil.deleteLoginToken(accountLogoutDTO.getUserId());
return Boolean.TRUE; return Boolean.TRUE;
} }
@Override @Override
public Boolean isLogin(AccountLogoutDTO accountLogoutDTO) { public Boolean isLogin(AccountLogoutDTO accountLogoutDTO) {
String token = LocalCacheUtils.getTokenCache(String.valueOf(accountLogoutDTO.getUserId())); String userIdStr = String.valueOf(accountLogoutDTO.getUserId());
// 先查本地缓存
String token = LocalCacheUtils.getTokenCache(userIdStr);
if (StringUtils.isNotBlank(token)) { if (StringUtils.isNotBlank(token)) {
return Boolean.TRUE; return Boolean.TRUE;
} }
return Boolean.FALSE; // 本地没有时,再查 Redis保证服务重启后也能判断登录状态
String redisToken = redisUtil.getLoginToken(accountLogoutDTO.getUserId());
return StringUtils.isNotBlank(redisToken);
} }
@Override @Override
@@ -812,6 +830,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
if (StringUtils.isNotBlank(token)) { if (StringUtils.isNotBlank(token)) {
LocalCacheUtils.delTokenCache(String.valueOf(accountDelete.getId())); LocalCacheUtils.delTokenCache(String.valueOf(accountDelete.getId()));
} }
// 删除 Redis 中对应的登录 token
redisUtil.deleteLoginToken(accountDelete.getId());
if (!userName.equals(userToBeUpdate.getUserName())) { if (!userName.equals(userToBeUpdate.getUserName())) {
// accountMapper.deleteById(accountDelete); // accountMapper.deleteById(accountDelete);
log.info("排查用户被删除原因deleteNoLoginRequiredtrue, 删除用户(改为降为游客)"); log.info("排查用户被删除原因deleteNoLoginRequiredtrue, 删除用户(改为降为游客)");
@@ -1065,6 +1085,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
if (StringUtils.isNotBlank(token)) { if (StringUtils.isNotBlank(token)) {
LocalCacheUtils.delTokenCache(String.valueOf(account.getId())); LocalCacheUtils.delTokenCache(String.valueOf(account.getId()));
} }
// 删除 Redis 中对应的登录 token
redisUtil.deleteLoginToken(account.getId());
// accountMapper.deleteById(account.getId()); // accountMapper.deleteById(account.getId());
log.info("排查用户被删除原因deleteNoLoginRequiredNew删除用户改为降为游客"); log.info("排查用户被删除原因deleteNoLoginRequiredNew删除用户改为降为游客");
accountMapper.toVisitor(account.getId()); accountMapper.toVisitor(account.getId());
@@ -1269,7 +1291,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
account.setSystemUser(3); account.setSystemUser(3);
account.setIsTrial(1); account.setIsTrial(1);
account.setCredits(BigDecimal.valueOf(50)); account.setCredits(BigDecimal.valueOf(50));
account.setValidEndTime(toDayEnd(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli())); // 广场用户试用延长至7天
account.setValidEndTime(toDayEnd(Instant.now().plus(7, ChronoUnit.DAYS).toEpochMilli()));
account.setIsBeginner(1); account.setIsBeginner(1);
account.setValidStartTime(Instant.now().toEpochMilli()); account.setValidStartTime(Instant.now().toEpochMilli());
account.setCreateDate(new Date()); account.setCreateDate(new Date());
@@ -1933,6 +1956,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
return baseMapper.selectList(queryWrapper); return baseMapper.selectList(queryWrapper);
} }
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void toVisitor(Account account) { public void toVisitor(Account account) {
accountMapper.toVisitor(account.getId()); accountMapper.toVisitor(account.getId());
} }
@@ -2491,7 +2515,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()); subAccount.setValidEndTime(adminAcc.getValidEndTime());
} }
@@ -3553,7 +3577,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
// 只有当前子账号数量为0时允许批量上传 // 只有当前子账号数量为0时允许批量上传
QueryWrapper<Account> qw = new QueryWrapper<>(); QueryWrapper<Account> qw = new QueryWrapper<>();
qw.lambda().eq(Account::getSystemUser, 8) qw.lambda().eq(Account::getSystemUser, 8)
.eq(Account::getOrganizationId, parent.getOrganizationId()); .eq(Account::getOrganizationId, parent.getOrganizationId())
.eq(Account::getSubscriptionPlanId, parent.getSubscriptionPlanId());
List<Account> accounts = accountMapper.selectList(qw); List<Account> accounts = accountMapper.selectList(qw);
if (!accounts.isEmpty()) { if (!accounts.isEmpty()) {
throw new BusinessException("permit.bulk.creation", ResultEnum.PROMPT.getCode()); throw new BusinessException("permit.bulk.creation", ResultEnum.PROMPT.getCode());

View File

@@ -520,9 +520,10 @@ public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementM
.filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName())) .filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName()))
.map(DesignCollectionPrintElementDTO::getId) .map(DesignCollectionPrintElementDTO::getId)
.collect(Collectors.toList()); .collect(Collectors.toList());
List<CollectionElement> printBoardElements = new ArrayList<>();
if (!CollectionUtils.isEmpty(printBoardIds)) { if (!CollectionUtils.isEmpty(printBoardIds)) {
// 从数据库批量查询printBoard元素 // 从数据库批量查询printBoard元素
List<CollectionElement> printBoardElements = collectionElementMapper.selectBatchIds(printBoardIds); printBoardElements = collectionElementMapper.selectBatchIds(printBoardIds);
// 验证查询结果的完整性 // 验证查询结果的完整性
if (CollectionUtil.isEmpty(printBoardElements) || printBoardElements.size() != printBoardIds.size()) { if (CollectionUtil.isEmpty(printBoardElements) || printBoardElements.size() != printBoardIds.size()) {
throw new BusinessException("get.printBoards.data.is.mismatch"); throw new BusinessException("get.printBoards.data.is.mismatch");
@@ -543,7 +544,8 @@ public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementM
Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards() Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards()
.stream() .stream()
.collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v)); .collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v));
libraryCollectionElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap)); printBoardElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap));
// libraryCollectionElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap));
} }
} }
@@ -559,7 +561,8 @@ public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementM
Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards() Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards()
.stream() .stream()
.collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v)); .collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v));
generateCollectionElements.addAll(covertGeneratesToPrintCollections(generateDetailList, idToMap)); printBoardElements.addAll(covertGeneratesToPrintCollections(generateDetailList, idToMap));
// generateCollectionElements.addAll(covertGeneratesToPrintCollections(generateDetailList, idToMap));
} }
} }
} }

View File

@@ -33,7 +33,6 @@ import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.*; import org.apache.poi.xssf.usermodel.*;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -149,7 +148,17 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|| ADMIN_IDS.contains(account.getId()) || ADMIN_IDS.contains(account.getId())
|| ADMIN_IDS_READ_ONLY.contains(account.getId()) || ADMIN_IDS_READ_ONLY.contains(account.getId())
)) { )) {
if (StringUtil.isNullOrEmpty(startTime)) startTime = "2024-02-01 00:00:00"; boolean filterBySecond ;
if (StringUtil.isNullOrEmpty(startTime)) {
startTime = "2024-02-01 00:00:00";
filterBySecond = true;
} else {
LocalDateTime thresholdTime = LocalDateTime.of(2024, 5, 1, 0, 0, 0);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime startDateTime = LocalDateTime.parse(startTime, formatter);
filterBySecond = startDateTime.isBefore(thresholdTime);
}
if (StringUtil.isNullOrEmpty(endTime)) { if (StringUtil.isNullOrEmpty(endTime)) {
// yyyy-MM-dd HH:mm:ss "HH"表示24小时制 "hh"表示12小时制 // yyyy-MM-dd HH:mm:ss "HH"表示24小时制 "hh"表示12小时制
endTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); endTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
@@ -173,7 +182,7 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
default: default:
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode()); throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode());
} }
return designMapper.getDesignStatistic(startTime, endTime, ids, email, role, account.getOrganizationName()); return designMapper.getDesignStatistic(startTime, endTime, ids, email, role, account.getOrganizationName(), filterBySecond);
} else { } else {
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode()); throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode());
} }
@@ -695,14 +704,19 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
String username = UserContext.getUserHolder().getUsername(); String username = UserContext.getUserHolder().getUsername();
Account account = new Account(); Account account = new Account();
account.setId(accountId);
// 修改用户有效期截止日期、用户类型、积分 // 修改用户有效期截止日期、用户类型、积分
if (!Objects.isNull(validEndTime)) { if (!Objects.isNull(validEndTime)) {
account.setValidEndTime(validEndTime); account.setValidEndTime(validEndTime);
log.info("管理员:{},修改用户 {} 信息,将账号到期时间置为:{}", username, accountId, validEndTime); log.info("管理员:{},修改用户 {} 信息,将账号到期时间置为:{}", username, accountId, validEndTime);
} }
if (!Objects.isNull(systemUser)) { if (!Objects.isNull(systemUser) && !systemUser.equals(0)) {
account.setSystemUser(systemUser); account.setSystemUser(systemUser);
log.info("管理员:{},修改用户 {} 信息,将账号身份置为:{}", username, accountId, systemUser); log.info("管理员:{},修改用户 {} 信息,将账号身份置为:{}", username, accountId, systemUser);
} else if (systemUser.equals(0)){
// 将用户身份设置为游客
accountService.toVisitor(account);
return true;
} }
/*if (!StringUtils.isNullOrEmpty(systemUser)) { /*if (!StringUtils.isNullOrEmpty(systemUser)) {
int systemUser = 0; int systemUser = 0;
@@ -728,7 +742,6 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
} }
// todo 如果修改管理员账号的积分上限或子账号数量,则其所有子账号的积分上限需要重新计算 // todo 如果修改管理员账号的积分上限或子账号数量,则其所有子账号的积分上限需要重新计算
account.setId(accountId);
account.setUpdateDate(new Date()); account.setUpdateDate(new Date());
return accountMapper.updateById(account) == 1; return accountMapper.updateById(account) == 1;
// accountService.update(account,null); // accountService.update(account,null);
@@ -798,27 +811,46 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
return accountMapper.selectPage(new Page<>(queryUserConditionsVO.getPage(), queryUserConditionsVO.getSize()), queryWrapper); 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(); Long accountId = UserContext.getUserHolder().getId();
Account account = accountMapper.selectById(accountId); Account account = accountMapper.selectById(accountId);
// 允许查看数据的用户id
if (Objects.nonNull(account.getSystemUser()) // 权限校验
&& (account.getSystemUser().equals(5) if (Objects.isNull(account.getSystemUser())
|| account.getSystemUser().equals(7) || (!account.getSystemUser().equals(5)
|| ADMIN_IDS.contains(account.getId()) && !account.getSystemUser().equals(7)
|| ADMIN_IDS_READ_ONLY.contains(account.getId()) && !ADMIN_IDS.contains(account.getId())
)) { && !ADMIN_IDS_READ_ONLY.contains(account.getId()))) {
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode());
}
// 创建分页对象
Page<Account> page = new Page<>(pageNum, pageSize);
// 构建查询条件
QueryWrapper<Account> queryWrapper = new QueryWrapper<>(); QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id as value, user_name as label"); queryWrapper.select("id", "user_name", "user_email");
if ((account.getSystemUser().equals(7) || account.getSystemUser().equals(5)) if ((account.getSystemUser().equals(7) || account.getSystemUser().equals(5))
&& !StringUtil.isNullOrEmpty(account.getOrganizationName())) { && !StringUtil.isNullOrEmpty(account.getOrganizationName())) {
queryWrapper.lambda().eq(Account::getOrganizationName, account.getOrganizationName()); queryWrapper.lambda().eq(Account::getOrganizationName, account.getOrganizationName());
} }
return accountMapper.selectMaps(queryWrapper);
} else { if (!StringUtil.isNullOrEmpty(email)) {
throw new BusinessException("have.no.permission", ResultEnum.PROMPT.getCode()); queryWrapper.lambda().like(Account::getUserEmail, email);
} }
// return maps.stream().map(map -> (Long)map.get("id")).collect(Collectors.toList());
// 执行分页查询
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;
});
} }
/** /**
@@ -838,19 +870,20 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
if (!StringUtil.isNullOrEmpty(queryPaymentInfoDTO.getOrder()) && queryPaymentInfoDTO.getOrder().equals("ASC")) { if (!StringUtil.isNullOrEmpty(queryPaymentInfoDTO.getOrder()) && queryPaymentInfoDTO.getOrder().equals("ASC")) {
order = "ASC"; order = "ASC";
} }
String status = StringUtil.isNullOrEmpty(queryPaymentInfoDTO.getStatus()) ? "Success" : queryPaymentInfoDTO.getStatus();
List<PaymentInfoVO> paymentInfoVOS = paymentInfoMapper.queryPaymentInfo(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(), List<PaymentInfoVO> paymentInfoVOS = paymentInfoMapper.queryPaymentInfo(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
queryPaymentInfoDTO.getType(), queryPaymentInfoDTO.getStatus(), queryPaymentInfoDTO.getType(), status,
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(), queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(),
size, offset, order, queryPaymentInfoDTO.getPayer()); size, offset, order, queryPaymentInfoDTO.getPayer());
// 查询数据总量 // 查询数据总量
Long total = paymentInfoMapper.queryPaymentInfoCount(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(), Long total = paymentInfoMapper.queryPaymentInfoCount(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
queryPaymentInfoDTO.getType(), queryPaymentInfoDTO.getStatus(), queryPaymentInfoDTO.getType(), status,
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(), queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getPayer()); queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getPayer());
// 查询符合查询条件的总金额 // 查询符合查询条件的总金额
BigDecimal payerTotal = paymentInfoMapper.queryTotalPaymentAmount(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(), BigDecimal payerTotal = paymentInfoMapper.queryTotalPaymentAmount(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
queryPaymentInfoDTO.getType(), queryPaymentInfoDTO.getStatus(), queryPaymentInfoDTO.getType(), status,
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(), queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getPayer()); queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getPayer());
// 总页数 // 总页数

View File

@@ -363,9 +363,9 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
public List<TDesignPythonOutfitDetail> saveDesignSingleItemDetailAndLayers(DesignPythonObjects pythonObjects public List<TDesignPythonOutfitDetail> saveDesignSingleItemDetailAndLayers(DesignPythonObjects pythonObjects
, Long designId, Long designItemId, Long userId , Long designId, Long designItemId, Long userId
, JSONObject outfit, String timeZone, List<DesignSingleItemDTO> designSingleItemDTOList , JSONObject outfit, String timeZone, List<DesignSingleItemDTO> designSingleItemDTOList
, Map<String, List<String>> priorityAndUndividedLayer /*, Map<String, List<String>> priorityAndUndividedLayer*/
, boolean changeModelFlag , boolean changeModelFlag
, Long modelId, String modelType, boolean isSingleCollectionFlag) { , Long modelId, String modelType, boolean isSingleCollectionFlag, String designType) {
DesignItem designItem = new DesignItem(); DesignItem designItem = new DesignItem();
// String url = pythonObjects.getObjects().get(0).getBasic().getSave_name(); // 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.setColor(detail.getColor());
designItemDetail.setIconPath(detail.getIcon()); designItemDetail.setIconPath(detail.getIcon());
// designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getType().toLowerCase())); // designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getType().toLowerCase()));
/*// 取消存储UndividedLayer和UndividedLayerWithSinglePrint字段
if (!detail.getType().equals("Body")) { if (!detail.getType().equals("Body")) {
designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(0)); if (!StringUtil.isNullOrEmpty(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(0))) {
designItemDetail.setUndividedLayerWithSinglePrint(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(1)); 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表中 这里还要存吗? // 印花存储在design_item_detail_print表中 这里还要存吗?
// DesignPythonItemPrint printObject = detail.getPrintToPython(); // DesignPythonItemPrint printObject = detail.getPrintToPython();
// designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath()); // designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath());
@@ -436,7 +440,18 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
// designItemDetail.setPrintJson(JSON.toJSONString(printObject)); // designItemDetail.setPrintJson(JSON.toJSONString(printObject));
designItemDetail.setPartialDesign(Objects.isNull(detail.getPrint()) ? null : detail.getPrint().getPartial()); designItemDetail.setPartialDesign(Objects.isNull(detail.getPrint()) ? null : detail.getPrint().getPartial());
designItemDetail.setGradientString(detail.getGradientString());
designItemDetails.add(designItemDetail); 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,11 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
priorityOffset = designSingleItemDTOList.stream() priorityOffset = designSingleItemDTOList.stream()
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset)); .collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset));
} }
// 创建 priority 到 DesignSingleItemDTO 的映射,用于获取 transpose 和 rotate
Map<Integer, DesignSingleItemDTO> priorityToItemDTOMap = designSingleItemDTOList.stream()
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, dto -> dto, (old, newVal) -> old));
List<TDesignPythonOutfitDetail> list = new ArrayList<>(); List<TDesignPythonOutfitDetail> list = new ArrayList<>();
for (int i = 0; i < layers.size(); i++) { for (int i = 0; i < layers.size(); i++) {
JSONObject jsonObject = layers.getJSONObject(i); JSONObject jsonObject = layers.getJSONObject(i);
@@ -509,6 +529,12 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(priority)))); designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(priority))));
} }
designPythonOutfitDetail.setPriority(priority); designPythonOutfitDetail.setPriority(priority);
// 从前端传入的 DesignSingleItemDTO 中获取 transpose 和 rotate不再从 Python 返回的数据获取
DesignSingleItemDTO itemDTO = priorityToItemDTOMap.get(Math.abs(priority));
if (itemDTO != null) {
designPythonOutfitDetail.setTranspose(itemDTO.getTranspose() != null ? Arrays.toString(itemDTO.getTranspose()) : null);
designPythonOutfitDetail.setRotate(itemDTO.getRotate());
}
list.add(designPythonOutfitDetail); list.add(designPythonOutfitDetail);
} }
@@ -683,6 +709,9 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public DesignSingleVO designSingleIncludeLayers(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) { 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数据太长所以这里去掉 // 记录入参 base64数据太长所以这里去掉
DesignSingleIncludeLayersDTO clone = SerializationUtils.clone(designSingleIncludeLayersDTO); DesignSingleIncludeLayersDTO clone = SerializationUtils.clone(designSingleIncludeLayersDTO);
clone.getDesignSingleItemDTOList().forEach(i -> { clone.getDesignSingleItemDTOList().forEach(i -> {
@@ -706,6 +735,17 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
log.info("set partialDesignBase64为空便于日志打印"); log.info("set partialDesignBase64为空便于日志打印");
i.getPartialDesign().setPartialDesignBase64(null); 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)); log.info("designSingle request入参 ==> " + JSONObject.toJSONString(clone));
@@ -800,7 +840,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
maskBase64ToPath(designSingleIncludeLayersDTO, setNull); maskBase64ToPath(designSingleIncludeLayersDTO, setNull);
// maskBase64ToPath(designSingleIncludeLayersDTO, Boolean.TRUE); // maskBase64ToPath(designSingleIncludeLayersDTO, Boolean.TRUE);
partialDesignBase64ToImage(designSingleIncludeLayersDTO, userId, designSingleIncludeLayersDTO.getIsPreview()); partialDesignBase64ToImage(designSingleIncludeLayersDTO, userId, designSingleIncludeLayersDTO.getIsPreview(), designSingleIncludeLayersDTO.getDesignType());
// 组装入参 // 组装入参
DesignPythonObjects objects = pythonService.covertDesignSingleParam( DesignPythonObjects objects = pythonService.covertDesignSingleParam(
@@ -818,13 +858,13 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
JSONObject outfit = data.getJSONObject("0"); JSONObject outfit = data.getJSONObject("0");
JSONArray layers = outfit.getJSONArray("layers"); JSONArray layers = outfit.getJSONArray("layers");
Map<String, List<String>> priorityAndUndividedLayer = setPriorityAndUndividedLayer(layers); // Map<String, List<String>> priorityAndUndividedLayer = setPriorityAndUndividedLayer(layers, designSingleIncludeLayersDTO);
if (!designSingleIncludeLayersDTO.getIsPreview()) { if (!designSingleIncludeLayersDTO.getIsPreview()) {
// 更新及保存图层信息 // 更新及保存图层信息
tDesignPythonOutfitDetails = saveDesignSingleItemDetailAndLayers(objects, design.getId(), designSingleIncludeLayersDTO.getDesignItemId() tDesignPythonOutfitDetails = saveDesignSingleItemDetailAndLayers(objects, design.getId(), designSingleIncludeLayersDTO.getDesignItemId()
, userId, outfit, designSingleIncludeLayersDTO.getTimeZone() , userId, outfit, designSingleIncludeLayersDTO.getTimeZone()
, designSingleIncludeLayersDTO.getDesignSingleItemDTOList() , designSingleIncludeLayersDTO.getDesignSingleItemDTOList()
, priorityAndUndividedLayer, changeModelFlag, modelId, modelType, isSingleCollectionFlag); /*, priorityAndUndividedLayer*/, changeModelFlag, modelId, modelType, isSingleCollectionFlag, designSingleIncludeLayersDTO.getDesignType());
saveCollectionElement(designSingleIncludeLayersDTO); saveCollectionElement(designSingleIncludeLayersDTO);
} else { } else {
@@ -858,8 +898,8 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
outfit.getString("synthesis_url"), outfit.getString("synthesis_url"),
designSingleIncludeLayersDTO.getDesignSingleItemDTOList(), designSingleIncludeLayersDTO.getDesignSingleItemDTOList(),
detailsVO, detailsVO,
design.getSingleOverall(), design.getSingleOverall()/*,
priorityAndUndividedLayer); priorityAndUndividedLayer*/);
} }
// 方法1仅查询无事务 // 方法1仅查询无事务
@@ -919,12 +959,12 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
item.setMaskUrl(path); 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 -> { designSingleIncludeLayersDTO.getDesignSingleItemDTOList().forEach(item -> {
PartialDesignDTO partialDesignDTO = item.getPartialDesign(); PartialDesignDTO partialDesignDTO = item.getPartialDesign();
if (!Objects.isNull(item.getPartialDesign()) if (!Objects.isNull(item.getPartialDesign())
@@ -946,21 +986,90 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
item.getPartialDesign().setPartialDesignMinioPath(newPath); item.getPartialDesign().setPartialDesignMinioPath(newPath);
} else if (Objects.isNull(item.getPartialDesign()) } else if (Objects.isNull(item.getPartialDesign())
|| StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignMinioPath())) { || StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignMinioPath())) {
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)); 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 @Override
public Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers) { 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<>(); HashMap<String, List<String>> priorityAndLayer = new HashMap<>();
if (designType.equals("default")) {
for (int i = 0; i < layers.size(); i++) { for (int i = 0; i < layers.size(); i++) {
JSONObject jsonObject = layers.getJSONObject(i); JSONObject jsonObject = layers.getJSONObject(i);
String priority = jsonObject.getString("priority"); String priority = jsonObject.getString("priority");
String category = jsonObject.getString("image_category").split("_")[0]; String category = jsonObject.getString("image_category").split("_")[0];
if (!category.equals("body") && !priorityAndLayer.containsKey(priority)) 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"))); 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; return priorityAndLayer;
} }
@@ -1075,8 +1184,8 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
String currentFullBodyView, String currentFullBodyView,
List<DesignSingleItemDTO> designSingleItemDTOList, List<DesignSingleItemDTO> designSingleItemDTOList,
List<DesignPythonOutfitVO> layersObject, List<DesignPythonOutfitVO> layersObject,
String singleOrOverall, String singleOrOverall/*,
Map<String, List<String>> priorityAndUndividedLayer) { Map<String, List<String>> priorityAndUndividedLayer*/) {
DesignSingleVO designSingleVO = new DesignSingleVO(); DesignSingleVO designSingleVO = new DesignSingleVO();
ArrayList<DesignItemClothesDetailVO> clothes = new ArrayList<>(); ArrayList<DesignItemClothesDetailVO> clothes = new ArrayList<>();
@@ -1116,10 +1225,15 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
String preSignedUrl = StringUtil.isNullOrEmpty(partialDesignMinioPath) ? null : minioUtil.getPreSignedUrl(partialDesignMinioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true); String preSignedUrl = StringUtil.isNullOrEmpty(partialDesignMinioPath) ? null : minioUtil.getPreSignedUrl(partialDesignMinioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true);
designItemClothesDetailVO.setPartialDesign(new PartialDesignDTO(partialDesignMinioPath, preSignedUrl)); designItemClothesDetailVO.setPartialDesign(new PartialDesignDTO(partialDesignMinioPath, preSignedUrl));
/*// 取消存储/返回UndividedLayer和UndividedLayerWithSinglePrint字段
if (priorityAndUndividedLayer.containsKey(singleItem.getPriority().toString())) { if (priorityAndUndividedLayer.containsKey(singleItem.getPriority().toString())) {
designItemClothesDetailVO.setUndividedLayer(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(0), 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)); 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())); body.setLayersObject(layersObject.stream().filter(layers -> layers.getImageCategory().equals("body")).collect(Collectors.toList()));
clothes.add(designItemClothesDetailVO); clothes.add(designItemClothesDetailVO);
@@ -1201,6 +1315,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
designItemDetailPrint.setPosition(print.getLocation().toString()); designItemDetailPrint.setPosition(print.getLocation().toString());
designItemDetailPrint.setAngle(print.getAngle()); designItemDetailPrint.setAngle(print.getAngle());
designItemDetailPrint.setPriority(priority); designItemDetailPrint.setPriority(priority);
designItemDetailPrint.setObject(print.getObject());
designItemDetailPrints.add(designItemDetailPrint); designItemDetailPrints.add(designItemDetailPrint);
}); });
@@ -1294,6 +1409,9 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
} }
} }
if (Objects.isNull(designSingleItem.getPrintObject()) || Objects.isNull(designSingleItem.getPrintObject().getPrints())) {
return;
}
// 添加print到library // 添加print到library
designSingleItem.getPrintObject().getPrints().forEach(print -> { designSingleItem.getPrintObject().getPrints().forEach(print -> {
if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Collection")) { if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Collection")) {

View File

@@ -23,6 +23,7 @@ import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -45,6 +46,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
@@ -100,8 +102,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
private final UserLikeGroupService userLikeGroupService; private final UserLikeGroupService userLikeGroupService;
private final UserLikeService userLikeService; private final UserLikeService userLikeService;
private final UserBehaviorMapper userBehaviorMapper; private final UserBehaviorMapper userBehaviorMapper;
private final UserPreferenceLogMapper userPreferenceLogMapper; private final UserPreferenceMapper userPreferenceMapper;
private final WorkspaceService workspaceService; private final WorkspaceService workspaceService;
private final WorkspaceRelStyleMapper workspaceRelStyleMapper;
@Value("${minio.endpoint}") @Value("${minio.endpoint}")
private String endpoint; private String endpoint;
@@ -713,7 +716,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
(existing, replacement) -> replacement)); (existing, replacement) -> replacement));
Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers); Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
log.info("all typeLayers Map:{}", typeAndUndividedLayer); 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()) { for (DesignPythonItem detail : item.getItems()) {
if (null == detail) { if (null == detail) {
continue; continue;
@@ -851,7 +854,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
Map<String, Integer> typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0], Map<String, Integer> typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0],
d -> Math.abs(d.getPriority()), d -> Math.abs(d.getPriority()),
(existing, replacement) -> replacement)); (existing, replacement) -> replacement));
Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers); // Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
for (DesignPythonItem detail : item.getItems()) { for (DesignPythonItem detail : item.getItems()) {
if (null == detail) { if (null == detail) {
continue; continue;
@@ -862,9 +865,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
designItemDetail.setDesignItemId(designItemId); designItemDetail.setDesignItemId(designItemId);
designItemDetail.setCollectionElementId(detail.getElementId()); designItemDetail.setCollectionElementId(detail.getElementId());
designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone)); designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
if (!detail.getType().equals("Body")) { /*if (!detail.getType().equals("Body")) {
designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType())); designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType()));
} }*/
if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) { if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
designItemDetail.setPath(detail.getBody_path()); designItemDetail.setPath(detail.getBody_path());
@@ -1108,18 +1111,20 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
//修改designItem为like状态 //修改designItem为like状态
designItemService.updateLikeStatus(designLikeDTO.getDesignItemId(), (byte) 1); designItemService.updateLikeStatus(designLikeDTO.getDesignItemId(), (byte) 1);
// 记录喜欢的系统sketch // 记录喜欢的系统sketch
addSystemLikeSketch(designItem); addSystemLikeSketch(designItem, designLikeDTO.getProjectId());
// 更新项目更新时间 // 更新项目更新时间
projectService.modifyProjectUpdateTime(designLikeDTO.getProjectId()); projectService.modifyProjectUpdateTime(designLikeDTO.getProjectId());
return new DesignLikeVO(userLikeSortId, userGroupId, groupDetailId, pictureName, userLike.getId(), userLikeSort.getSort()); 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(); AuthPrincipalVo userHolder = UserContext.getUserHolder();
QueryWrapper<DesignItemDetail> qw = new QueryWrapper<>(); QueryWrapper<DesignItemDetail> qw = new QueryWrapper<>();
qw.lambda().eq(DesignItemDetail::getDesignItemId, designItem.getId()); qw.lambda().eq(DesignItemDetail::getDesignItemId, designItem.getId());
qw.lambda().ne(DesignItemDetail::getType, "Body"); qw.lambda().ne(DesignItemDetail::getType, "Body");
List<DesignItemDetail> designItemDetails = designItemDetailMapper.selectList(qw); List<DesignItemDetail> designItemDetails = designItemDetailMapper.selectList(qw);
List<WorkspaceRelStyle> workspaceRelStyles = workspaceRelStyleMapper.selectByProjectId(projectId);
for (DesignItemDetail designItemDetail : designItemDetails) { for (DesignItemDetail designItemDetail : designItemDetails) {
if (designItemDetail.getPath().startsWith("aida-sys-image")) { if (designItemDetail.getPath().startsWith("aida-sys-image")) {
String[] split = designItemDetail.getPath().split("/"); String[] split = designItemDetail.getPath().split("/");
@@ -1132,16 +1137,34 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
userBehavior.setCreateTime(LocalDateTime.now()); userBehavior.setCreateTime(LocalDateTime.now());
userBehaviorMapper.insert(userBehavior); userBehaviorMapper.insert(userBehavior);
UserPreferenceLogTest userPreferenceLogTest = new UserPreferenceLogTest(); UserPreference userPreference = new UserPreference();
userPreferenceLogTest.setPath(designItemDetail.getPath()); userPreference.setPath(designItemDetail.getPath());
userPreferenceLogTest.setAccountId(userHolder.getId()); SysFile sysFile = sysFileService.getOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUrl, designItemDetail.getPath()));
// userPreferenceLogTest.setUserLikeGroupId(userLike.getUserLikeGroupId()); userPreference.setAccountId(userHolder.getId());
userPreferenceLogTest.setDataTime(designItemDetail.getCreateDate().toInstant() 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;
}
userPreference.setDataTime(new Date().toInstant()
.atZone(ZoneId.systemDefault()) .atZone(ZoneId.systemDefault())
.toLocalDateTime()); .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) { private List<Long> validateMergeElement(List<CollectionElement> oldElements, List<DesignItemDetail> designItemDetails) {
@@ -1309,12 +1332,13 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
d.setScope(o.getPath().startsWith("aida-sys-image") ? "sys" : "user"); d.setScope(o.getPath().startsWith("aida-sys-image") ? "sys" : "user");
d.setLevel1Type(converTypeToLevel1(o.getType())); d.setLevel1Type(converTypeToLevel1(o.getType()));
d.setGradient(JSONObject.parseObject(o.getGradientString(), Gradient.class)); d.setGradient(JSONObject.parseObject(o.getGradientString(), Gradient.class));
/*// 取消存储/返回UndividedLayer和UndividedLayerWithSinglePrint字段
if (!StringUtil.isNullOrEmpty(o.getUndividedLayer())) { if (!StringUtil.isNullOrEmpty(o.getUndividedLayer())) {
d.setUndividedLayer(minioUtil.getPreSignedUrl(o.getUndividedLayer(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); d.setUndividedLayer(minioUtil.getPreSignedUrl(o.getUndividedLayer(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
} }
if (!StringUtil.isNullOrEmpty(o.getUndividedLayerWithSinglePrint())) { if (!StringUtil.isNullOrEmpty(o.getUndividedLayerWithSinglePrint())) {
d.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(o.getUndividedLayerWithSinglePrint(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); d.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(o.getUndividedLayerWithSinglePrint(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
} }*/
// 根据designItemDetailId获取印花 // 根据designItemDetailId获取印花
List<DesignItemDetailPrint> prints = designItemDetailPrintService.getByDesignItemDetailId(o.getId(), "print"); List<DesignItemDetailPrint> prints = designItemDetailPrintService.getByDesignItemDetailId(o.getId(), "print");
prints.removeIf(print -> !minioUtil.doesObjectExist(print.getPath())); prints.removeIf(print -> !minioUtil.doesObjectExist(print.getPath()));
@@ -1563,6 +1587,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
} }
} }
designSinglePrint.setScale(scales); designSinglePrint.setScale(scales);
designSinglePrint.setObject(detailPrint.getObject());
prints.add(designSinglePrint); prints.add(designSinglePrint);
} else { } else {
// 多个印花 // 多个印花
@@ -1583,7 +1608,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
scales = Arrays.asList(scale, scale); scales = Arrays.asList(scale, scale);
} }
} }
prints.add(new DesignSinglePrint( DesignSinglePrint designSinglePrint = new DesignSinglePrint(
print.getLevel2Type(), print.getLevel2Type(),
minioUtil.getPreSignedUrl(print.getPath(), 24 * 60), minioUtil.getPreSignedUrl(print.getPath(), 24 * 60),
print.getPath(), print.getPath(),
@@ -1591,7 +1616,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
scales, scales,
print.getAngle(), print.getAngle(),
print.getPriority(), print.getPriority(),
ifSingle)); ifSingle);
designSinglePrint.setObject(print.getObject());
prints.add(designSinglePrint);
// } // }
}); });
} }
@@ -1643,6 +1670,24 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
.lt("create_date", endTime) .lt("create_date", endTime)
.select("count(id) as count"); .select("count(id) as count");
// 如果startTime早于2024-05-01 00:00:00添加额外的筛选条件
LocalDateTime thresholdTime = LocalDateTime.of(2024, 5, 1, 0, 0, 0);
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime startDateTime = LocalDateTime.parse(startTime, formatter);
if (startDateTime.isBefore(thresholdTime)) {
// 使用 notLike 来排除以 ":01" 或 ":02" 结尾的时间
// 方案2.1:使用 apply 方法执行数据库函数 create_date 字段的实际存储格式(可能包含毫秒),所以取最后三个字符进行匹配)
queryWrapper.apply("DATE_FORMAT(create_date, '%s') NOT IN ('01', '02')");
/*queryWrapper.notLike("create_date", "%:01")
.notLike("create_date", "%:02");*/
}
} catch (Exception e) {
log.warn("Failed to parse startTime: {}, skip time-based filtering", startTime, e);
}
if (!Objects.isNull(accountIds) && !accountIds.isEmpty()) { if (!Objects.isNull(accountIds) && !accountIds.isEmpty()) {
queryWrapper.in("account_id", accountIds); queryWrapper.in("account_id", accountIds);
} }
@@ -1650,7 +1695,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
List<Map<String, Object>> result = baseMapper.selectMaps(queryWrapper); List<Map<String, Object>> result = baseMapper.selectMaps(queryWrapper);
if (result != null && !result.isEmpty()) { if (result != null && !result.isEmpty()) {
Object countObj = result.get(0).get("count"); Object countObj = result.get(0).get("count");
return (Long) countObj; return countObj != null ? ((Number) countObj).longValue() : 0L;
} else { } else {
return 0L; return 0L;
} }
@@ -2519,7 +2564,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
Map<String, Integer> typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0], Map<String, Integer> typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0],
d -> Math.abs(d.getPriority()), d -> Math.abs(d.getPriority()),
(existing, replacement) -> replacement)); (existing, replacement) -> replacement));
Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers); // Map<String, String> typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
for (DesignPythonItem detail : item.getItems()) { for (DesignPythonItem detail : item.getItems()) {
if (null == detail) { if (null == detail) {
continue; continue;
@@ -2530,9 +2575,9 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
designItemDetail.setDesignItemId(designItemId); designItemDetail.setDesignItemId(designItemId);
designItemDetail.setCollectionElementId(detail.getElementId()); designItemDetail.setCollectionElementId(detail.getElementId());
designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone)); designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
if (!detail.getType().equals("Body")) { /*if (!detail.getType().equals("Body")) {
designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType())); designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType()));
} }*/
if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) { if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
designItemDetail.setPath(detail.getBody_path()); designItemDetail.setPath(detail.getBody_path());
//BODY不关联businessId //BODY不关联businessId

View File

@@ -48,6 +48,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import okhttp3.*; import okhttp3.*;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
@@ -59,10 +61,12 @@ import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
@@ -218,6 +222,8 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
version = "fast"; version = "fast";
params.put("version", "fast"); params.put("version", "fast");
} }
// 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中
saveGenerateImmediately(generate);
// 3.1 确定不同类型的印花分别调哪个接口 // 3.1 确定不同类型的印花分别调哪个接口
if (generateThroughImageTextDTO.getLevel1Type().equals(PRINT_BOARD.getRealName())) { if (generateThroughImageTextDTO.getLevel1Type().equals(PRINT_BOARD.getRealName())) {
switch (generateThroughImageTextDTO.getLevel2Type()) { switch (generateThroughImageTextDTO.getLevel2Type()) {
@@ -243,15 +249,28 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue); jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue);
} }
} else { } else {
if (Objects.equals(version, "fast")) {
GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(), GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(),
mode, category, generateThroughImageTextDTO.getGender(), version); mode, category, generateThroughImageTextDTO.getGender(), version);
jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue); jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue);
} else {
path = CommonConstant.GENERATE_PATH_FLUX2_KLEIN;
// 构建object_name: {userId}/{category}/{uuid}.png
String objectName = generateThroughImageTextDTO.getUserId() + "/" + category + "/" + UUID.randomUUID() + ".png";
ImageProcessRequest imageProcessRequest = ImageProcessRequest.builder()
.object_name(objectName)
.bucket_name(userBucket)
.prompt(text).build();
jsonString = JSON.toJSONString(imageProcessRequest);
} }
Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path); }
Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path, generateThroughImageTextDTO.getUniqueId());
// 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中
save(generate);
// 5、将本次请求存入redis // 5、将本次请求存入redis
String key = generateResultKey + ":" + generateThroughImageTextDTO.getUniqueId(); String key = generateResultKey + ":" + generateThroughImageTextDTO.getUniqueId();
@@ -266,6 +285,40 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
} }
public void saveGenerateImmediately(Generate generate) {
save(generate);
// 使用 TransactionSynchronizationManager 在事务真正提交后再设锁
// 否则 save() 完成后事务尚未 commitMQ 消费者立即读到 null
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
String lockKey = "generate:lock:" + generate.getUniqueId();
redisUtil.addToString(lockKey, "1", 60L);
log.debug("Save lock set after commit for uniqueId: {}", generate.getUniqueId());
}
});
}
private void waitForSaveLock(String uniqueId) {
String lockKey = "generate:lock:" + uniqueId;
int maxRetries = 30;
int retryIntervalMs = 200;
for (int i = 0; i < maxRetries; i++) {
if (Boolean.TRUE.equals(redisUtil.hasKey(lockKey))) {
log.debug("Save lock acquired for uniqueId: {} after {} retries", uniqueId, i);
return;
}
try {
Thread.sleep(retryIntervalMs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("Interrupted while waiting for save lock: {}", uniqueId);
return;
}
}
log.warn("Save lock timeout for uniqueId: {}, proceeding anyway", uniqueId);
}
public GenerateModeEnum getMode(GenerateThroughImageTextDTO generateThroughImageTextDTO) { public GenerateModeEnum getMode(GenerateThroughImageTextDTO generateThroughImageTextDTO) {
if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getText())) { if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getText())) {
if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) { if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) {
@@ -284,11 +337,16 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void processGenerateResult(String taskId, String url, String category) { public void processGenerateResult(String taskId, String url, String category) {
log.info("============ProcessGenerateResult listening==========");
log.debug("taskId: " + taskId);
String status = null;
// 1、处理模型返回的数据 // 1、处理模型返回的数据
GenerateDetail generateDetail = new GenerateDetail(); GenerateDetail generateDetail = new GenerateDetail();
GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO(); GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO();
Generate generate; Generate generate;
try { try {
// 等待 HTTP 线程写入完成后再查库
waitForSaveLock(taskId);
generate = selectByUniqueId(taskId); generate = selectByUniqueId(taskId);
} catch (MybatisPlusException e) { } catch (MybatisPlusException e) {
log.error(e.getMessage()); log.error(e.getMessage());
@@ -311,14 +369,15 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
generateDetail.setUrl(url); generateDetail.setUrl(url);
generateDetail.setGenerateId(generate.getId()); generateDetail.setGenerateId(generate.getId());
generateDetail.setCreateDate(LocalDateTime.now()); generateDetail.setCreateDate(LocalDateTime.now());
generateDetail.setMd5(md5); generateDetail.setMd5("");
// 将相应的url保存到数据库 // 将相应的url保存到数据库
generateDetailMapper.insert(generateDetail); generateDetailMapper.insert(generateDetail);
log.debug("generateDetail: " + generateDetail.toString());
// String uuid = taskId.substring(0, taskId.substring(0, taskId.lastIndexOf("-")).lastIndexOf("-")); // String uuid = taskId.substring(0, taskId.substring(0, taskId.lastIndexOf("-")).lastIndexOf("-"));
String key = generateResultKey + ":" + taskId; String key = generateResultKey + ":" + taskId;
String imageName = url.substring(url.lastIndexOf("/") + 1); String imageName = url.substring(url.lastIndexOf("/") + 1);
String status = imageName.equals("white_image.jpg") ? "Invalid" : "Success"; status = imageName.equals("white_image.jpg") ? "Invalid" : "Success";
if (StringUtil.isNullOrEmpty(category)) { if (StringUtil.isNullOrEmpty(category)) {
Generate generateRecord = selectByUniqueId(taskId); Generate generateRecord = selectByUniqueId(taskId);
category = generateRecord.getLevel2Type(); category = generateRecord.getLevel2Type();
@@ -326,6 +385,8 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), url, status, category); GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), url, status, category);
// 更新redis // 更新redis
redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
log.debug("generateResultVO: " + generateResultVO.toString());
// 执行积分扣除 // 执行积分扣除
// ** 注:如果生成的图片都是空白 则不扣积分 // ** 注:如果生成的图片都是空白 则不扣积分
@@ -785,8 +846,9 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
long requestEndTime = System.currentTimeMillis(); long requestEndTime = System.currentTimeMillis();
log.info("HTTP请求完成 - 响应状态: {}, 耗时: {}ms, taskId: {}", log.info("HTTP请求完成 - 响应状态: {}, 耗时: {}ms, taskId: {}",
response.code(), (requestEndTime - requestStartTime), taskId); response.code(), (requestEndTime - requestStartTime), taskId);
String result = response.body().string();
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
log.warn("Google API响应失败状态码: {} for taskId: {}", response.code(), taskId); log.warn("Google API响应失败状态码: {} for taskId: {},结果:{}", response.code(), taskId, result);
if (attempt < maxRetries) { if (attempt < maxRetries) {
Thread.sleep(retryDelay * attempt); // 递增延迟 Thread.sleep(retryDelay * attempt); // 递增延迟
continue; continue;
@@ -795,7 +857,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
} }
} }
String result = response.body().string();
// log.info("Google 响应结果:{}", result); // log.info("Google 响应结果:{}", result);
com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result); com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result);
@@ -1065,6 +1127,12 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
String result = response.body().string(); String result = response.body().string();
if (response.code() != 200) {
log.error("Google API 请求失败 - taskId: {}, 尝试: {}, URL: {}, 状态码: {}, 响应结果: {}",
taskId, attempt, endpoint, response.code(), result);
throw new BusinessException("system.error");
}
// log.info("Google 响应结果:{}", result); // log.info("Google 响应结果:{}", result);
com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result); com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result);
@@ -1203,6 +1271,9 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
* @param modelName advanced high normal * @param modelName advanced high normal
*/ */
private HashMap<String, String> chooseModelAndPrompt(GenerateThroughImageTextDTO generateDTO, String modelName) { private HashMap<String, String> chooseModelAndPrompt(GenerateThroughImageTextDTO generateDTO, String modelName) {
if (StringUtil.isNullOrEmpty(modelName)) {
throw new BusinessException("system error");
}
HashMap<String, String> modelAndPromptMap = new HashMap<>(); HashMap<String, String> modelAndPromptMap = new HashMap<>();
boolean isUseImage; boolean isUseImage;
if (Objects.isNull(generateDTO.getCollectionElementId()) if (Objects.isNull(generateDTO.getCollectionElementId())
@@ -1218,7 +1289,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
String style = generateDTO.getText().substring(0, firstCommaIndex).trim(); String style = generateDTO.getText().substring(0, firstCommaIndex).trim();
String prompt = generateDTO.getText().substring(firstCommaIndex + 1).trim(); String prompt = generateDTO.getText().substring(firstCommaIndex + 1).trim();
prompt = getPrintboardPrompt(style, prompt); prompt = getPrintboardPrompt(style, prompt, modelName, isUseImage);
modelAndPromptMap.put(ModelConstants.PROMPT, prompt); modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
@@ -1239,7 +1310,14 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
} }
} else if (ModelConstants.MOODBOARD.equals(generateDTO.getLevel1Type())) { } else if (ModelConstants.MOODBOARD.equals(generateDTO.getLevel1Type())) {
String prompt = generateDTO.getText() + "high-resolution, ultra-detailed, realistic textures, perfect anatomy, cinematic lighting, 8k render, editorial photography style"; String userInput = generateDTO.getText();
String systemPrompt = "high-resolution, ultra-detailed, realistic textures, cinematic lighting, 8k render, editorial photography style";
String prompt;
if (userInput == null || userInput.trim().isEmpty()) {
throw new BusinessException("prompt null");
} else {
prompt = "Theme: " + userInput.trim() + "\nRequirement: " + systemPrompt;
}
modelAndPromptMap.put(ModelConstants.PROMPT, prompt); modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
if (ModelConstants.ADVANCED.equals(modelName)) { if (ModelConstants.ADVANCED.equals(modelName)) {
@@ -1250,8 +1328,31 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL); modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL);
} }
} else if (ModelConstants.SKETCHBOARD.equals(generateDTO.getLevel1Type())) { } else if (ModelConstants.SKETCHBOARD.equals(generateDTO.getLevel1Type())) {
String prompt = generateDTO.getText() + "rules:front view sketch only,plain white background, single garment only, orthographic, centered on white background, borderless canvas, thin monochrome black line art.\n" + String style = "";
String userPrompt = "";
// 找到第一个逗号的位置
int firstCommaIndex = generateDTO.getText().indexOf(",");
if (firstCommaIndex != -1) {
// 截取第一个逗号前的内容作为style
style = generateDTO.getText().substring(0, firstCommaIndex).trim();
// 截取第一个逗号后的所有内容作为userPrompt去除首尾空格
userPrompt = generateDTO.getText().substring(firstCommaIndex + 1).trim();
if ("Lolita".equals(style)) {
style = "洛丽塔";
}
} else {
// 兼容无逗号的情况style为空全部内容作为userPrompt
userPrompt = generateDTO.getText().trim();
}
String prompt = userPrompt + "rules:front view sketch only,plain white background, single garment only, orthographic, centered on white background, borderless canvas, thin monochrome black line art.\n" +
" No clothes hanger, no fake clothes hanger, no human-related lines, no color fill, no words, no text, no black background, no boundary or frame."; " No clothes hanger, no fake clothes hanger, no human-related lines, no color fill, no words, no text, no black background, no boundary or frame.";
if (!style.trim().isEmpty() && !"all".equalsIgnoreCase(style)) {
prompt += ".sketch style:" + style.trim();
}
modelAndPromptMap.put(ModelConstants.PROMPT, prompt); modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
if (isUseImage) { if (isUseImage) {
if (ModelConstants.ADVANCED.equals(modelName)) { if (ModelConstants.ADVANCED.equals(modelName)) {
@@ -1449,6 +1550,11 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
if (imagePath != null) { if (imagePath != null) {
requestBuilder.image(finalImagePath1); requestBuilder.image(finalImagePath1);
} }
if (useModel.equals(ModelConstants.PRINTBOARD_HIGH_I2I)) {
GenerateImagesRequest.OptimizePromptOptions optimizePromptOptions = new GenerateImagesRequest.OptimizePromptOptions();
optimizePromptOptions.setMode("fast");
requestBuilder.optimizePromptOptions(optimizePromptOptions);
}
// 保存生成记录到数据库 // 保存生成记录到数据库
Generate generate = new Generate( Generate generate = new Generate(
@@ -1560,19 +1666,44 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
} }
private String getPrintboardPrompt(String style, String prompt) { private String getPrintboardPrompt(String style, String userInput, String modelName, boolean isUseImage) {
String systemPrompt = null;
String prompt;
if ("Painting Style".equals(style)) { if ("Painting Style".equals(style)) {
prompt = "1.Requirements: Create a seamless, tiling fashion printboard pattern for apparel. The output must be stylish, contemporary, and suitable for real garment printing. Design pattern, seamless, highly detailed, elegant composition, visually balanced, professional textile print\n" + if (ModelConstants.ADVANCED.equals(modelName)) {
"2.Core Theme: " + prompt + "\n" + systemPrompt = "Tileable seamless pattern, elegant composition, visually balanced, Light watercolor, Giplie Studio (style) pattern with even color field background, high-quality digital print, zero perspective depth, harmonious visual balance, consistent color tone.";
"3.Style: painting_style-The painting style refers to the overall approach, techniques, and artistic philosophy used in the artwork. For fashion designs that will be applied to printboards, it is important to define the unique stylistic elements that would translate well into wearable patterns."; } else if (ModelConstants.HIGH.equals(modelName)) {
systemPrompt = "Design pattern, seamless, highly detailed, elegant composition, visually balanced. \n" +
"Painting style: traditional painting, hand-painted, brush strokes.";
}
} else if ("Illustration Style".equals(style)) { } else if ("Illustration Style".equals(style)) {
prompt = "1.Requirements: Create a seamless, tiling fashion printboard pattern for apparel. The output must be stylish, contemporary, and suitable for real garment printing. Design pattern, seamless, highly detailed, elegant composition, visually balanced, professional textile print\n" + if (ModelConstants.ADVANCED.equals(modelName)) {
"2.Core Theme: " + prompt + "\n" + systemPrompt = "Tileable seamless pattern, elegant composition, visually balanced, flat graphic, clean lines pattern with even color field background, high-quality digital print, zero perspective depth, harmonious visual balance, consistent color tone. ";
"3.Style: illustration_style-Illustration style focuses on the visual storytellingaspect, often used to depict narratives, characters, or thematic concepts. Forfashion, this style can introduce vivid and artistic interpretations, often aligned with specific themes."; } else if (ModelConstants.HIGH.equals(modelName)) {
systemPrompt = "Design pattern, seamless, highly detailed, elegant composition, visually balanced. \n" +
"Illustration Style: flat graphic, clean lines.";
}
} else if ("Real Style".equals(style)) { } else if ("Real Style".equals(style)) {
prompt = "1.Requirements: Create a seamless, tiling fashion printboard pattern for apparel. The output must be stylish, contemporary, and suitable for real garment printing. Design pattern, seamless, highly detailed, elegant composition, visually balanced, professional textile print\n" + if (ModelConstants.ADVANCED.equals(modelName)) {
"2.Core Theme: " + prompt + "\n" + systemPrompt = "Tileable seamless pattern, even color field background, photorealistic style pattern, high-quality digital print, zero perspective depth, harmonious visual balance, consistent color tone. ";
"3.Style: real_style-Real style in fashion is all about authenticity. It featuresnatural fabrics, simple cuts that mirror real life silhouettes, and colors inspired bythe everyday world, exuding a down-to-earth and genuine charm."; } else if (ModelConstants.HIGH.equals(modelName)) {
systemPrompt = "Design pattern, seamless, highly detailed, elegant composition, visually balanced. \n" +
"Flat textile pattern printed directly on fabric surface, no three-dimensional objects, no items placed on cloth. \n" +
"Real style: fabric print, realistic woven/printed pattern, detailed surface pattern only";
}
} else {
throw new BusinessException("style error:" + style);
}
if (userInput == null || userInput.trim().isEmpty()) {
if (isUseImage) {
prompt = "Theme: Image content" + "\nRequirement: " + systemPrompt;
} else {
throw new BusinessException("prompt null");
}
} else {
prompt = "Theme: " + userInput.trim() + "\nRequirement: " + systemPrompt;
} }
return prompt; return prompt;
} }
@@ -1970,7 +2101,9 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
public Generate selectByUniqueId(String uniqueId) { public Generate selectByUniqueId(String uniqueId) {
QueryWrapper<Generate> qw = new QueryWrapper<>(); QueryWrapper<Generate> qw = new QueryWrapper<>();
qw.eq("unique_id", uniqueId); qw.eq("unique_id", uniqueId);
log.debug("selectByUniqueId: " + uniqueId);
Generate one = getOne(qw);
log.debug("Generate: " + one);
return getOne(qw); return getOne(qw);
} }
@@ -4264,7 +4397,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
MotionModeEnum motionModeEnum = MotionModeEnum.of(poseTransformDTO.getMode()); MotionModeEnum motionModeEnum = MotionModeEnum.of(poseTransformDTO.getMode());
switch (motionModeEnum) { switch (motionModeEnum) {
case POSE_TO_VIDEO: case POSE_TO_VIDEO:
params.put("pose_id", poseTransformDTO.getPoseId()); params.put("pose_id", poseTransformDTO.getPoseId().toString());
params.put("image_url", poseTransformDTO.getProductImage()); params.put("image_url", poseTransformDTO.getProductImage());
break; break;
case PROMPT_TO_VIDEO: case PROMPT_TO_VIDEO:

View File

@@ -0,0 +1,588 @@
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 org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.FileOutputStream;
import java.time.format.DateTimeFormatter;
@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.temp.dir}")
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
@Transactional(rollbackFor = Exception.class)
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_number 在并发下自增分配
final int maxAttempts = 5;
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
// 获取当前最大 contestant_number 并加行锁LIMIT 1 FOR UPDATE
QueryWrapper<Contestant> qMax = new QueryWrapper<>();
qMax.isNotNull("contestant_number");
qMax.orderByDesc("contestant_number");
qMax.last("LIMIT 1 FOR UPDATE");
Contestant last = contestantMapper.selectOne(qMax);
Integer nextNumber = (last == null || last.getContestantNumber() == null) ? 10000 : last.getContestantNumber() + 1;
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())
.videoDuration(request.getVideoDuration())
.videoSize(request.getVideoSize())
.pdfSize(request.getPdfSize())
.contestantNumber(nextNumber)
.createdAt(now)
.updatedAt(now)
.build();
contestantMapper.insert(toInsert);
resp.put("success", true);
sendSiteMsg(toInsert.getId(), toInsert.getEmail());
return resp;
} catch (Exception e) {
log.warn("Attempt {} to assign contestant_number failed", attempt, e);
String msg = e.getMessage() == null ? "" : e.getMessage().toLowerCase();
if ((msg.contains("duplicate") || msg.contains("uniq_contestant_number") || msg.contains("contestant_number")) && attempt < maxAttempts) {
try {
Thread.sleep(100L);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
continue;
}
throw e;
}
}
throw new BusinessException("Failed to assign contestant number after retries.");
} 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.setVideoDuration(request.getVideoDuration());
existing.setVideoSize(request.getVideoSize());
existing.setPdfSize(request.getPdfSize());
existing.setUpdatedAt(now);
contestantMapper.updateById(existing);
resp.put("success", true);
return resp;
}
}
@Override
public byte[] exportContestants() throws Exception {
List<Contestant> list = contestantMapper.selectList(new QueryWrapper<>());
try (XSSFWorkbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
Sheet sheet = workbook.createSheet("contestants");
int rowIdx = 0;
Row header = sheet.createRow(rowIdx++);
String[] headers = new String[] {
"contestantNumber", "email", "firstName", "lastName", "gender", "occupation",
"age", "countryRegionCity", "phoneNumber", "designTitle", "designDescription",
"pdfPath", "videoPath", "videoDuration", "videoSizeMB", "pdfSizeMB", "createdAt", "updatedAt"
};
for (int i = 0; i < headers.length; i++) {
Cell c = header.createCell(i);
c.setCellValue(headers[i]);
}
for (Contestant cst : list) {
Row r = sheet.createRow(rowIdx++);
int ci = 0;
r.createCell(ci++).setCellValue(cst.getContestantNumber() == null ? "" : cst.getContestantNumber().toString());
r.createCell(ci++).setCellValue(cst.getEmail() == null ? "" : cst.getEmail());
r.createCell(ci++).setCellValue(cst.getFirstName() == null ? "" : cst.getFirstName());
r.createCell(ci++).setCellValue(cst.getLastName() == null ? "" : cst.getLastName());
r.createCell(ci++).setCellValue(cst.getGender() == null ? "" : cst.getGender());
r.createCell(ci++).setCellValue(cst.getOccupation() == null ? "" : cst.getOccupation());
r.createCell(ci++).setCellValue(cst.getAge() == null ? "" : cst.getAge().toString());
r.createCell(ci++).setCellValue(cst.getCountryRegionCity() == null ? "" : cst.getCountryRegionCity());
r.createCell(ci++).setCellValue(cst.getPhoneNumber() == null ? "" : cst.getPhoneNumber());
r.createCell(ci++).setCellValue(cst.getDesignTitle() == null ? "" : cst.getDesignTitle());
r.createCell(ci++).setCellValue(cst.getDesignDescription() == null ? "" : cst.getDesignDescription());
// r.createCell(ci++).setCellValue(cst.getPdfPath() == null ? "" : cst.getPdfPath());
// r.createCell(ci++).setCellValue(cst.getVideoPath() == null ? "" : cst.getVideoPath());
// 视频时长(秒)
r.createCell(ci++).setCellValue(cst.getVideoDuration() == null ? "" : cst.getVideoDuration().toString());
// 视频大小、PDF 大小:以 MB 导出,保留两位小数
if (cst.getVideoSize() == null) {
r.createCell(ci++).setCellValue("");
} else {
double vMb = cst.getVideoSize() / 1024.0 / 1024.0;
r.createCell(ci++).setCellValue(String.format("%.2f", vMb));
}
if (cst.getPdfSize() == null) {
r.createCell(ci++).setCellValue("");
} else {
double pMb = cst.getPdfSize() / 1024.0 / 1024.0;
r.createCell(ci++).setCellValue(String.format("%.2f", pMb));
}
r.createCell(ci++).setCellValue(cst.getCreatedAt() == null ? "" : cst.getCreatedAt().toString());
r.createCell(ci++).setCellValue(cst.getUpdatedAt() == null ? "" : cst.getUpdatedAt().toString());
}
workbook.write(out);
out.flush();
return out.toByteArray();
} catch (IOException e) {
log.error("export contestants failed", e);
throw e;
}
}
@Override
public void saveContestantsToLocal() throws Exception {
List<Contestant> list = contestantMapper.selectList(new QueryWrapper<>());
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
String ts = LocalDateTime.now().format(fmt);
Path exportDir = Paths.get(uploadDir == null ? "uploads" : uploadDir, "exports");
Files.createDirectories(exportDir);
Path outPath = exportDir.resolve("contestants_" + ts + ".xlsx");
try (XSSFWorkbook workbook = new XSSFWorkbook(); FileOutputStream fos = new FileOutputStream(outPath.toFile())) {
Sheet sheet = workbook.createSheet("contestants");
int rowIdx = 0;
Row header = sheet.createRow(rowIdx++);
String[] headers = new String[] {
"contestantNumber", "email", "firstName", "lastName", "gender", "occupation",
"age", "countryRegionCity", "phoneNumber", "designTitle", "designDescription",
"pdfPath", "videoPath", "videoDuration", "videoSizeMB", "pdfSizeMB", "createdAt", "updatedAt"
};
for (int i = 0; i < headers.length; i++) {
Cell c = header.createCell(i);
c.setCellValue(headers[i]);
}
for (Contestant cst : list) {
Row r = sheet.createRow(rowIdx++);
int ci = 0;
r.createCell(ci++).setCellValue(cst.getContestantNumber() == null ? "" : cst.getContestantNumber().toString());
r.createCell(ci++).setCellValue(cst.getEmail() == null ? "" : cst.getEmail());
r.createCell(ci++).setCellValue(cst.getFirstName() == null ? "" : cst.getFirstName());
r.createCell(ci++).setCellValue(cst.getLastName() == null ? "" : cst.getLastName());
r.createCell(ci++).setCellValue(cst.getGender() == null ? "" : cst.getGender());
r.createCell(ci++).setCellValue(cst.getOccupation() == null ? "" : cst.getOccupation());
r.createCell(ci++).setCellValue(cst.getAge() == null ? "" : cst.getAge().toString());
r.createCell(ci++).setCellValue(cst.getCountryRegionCity() == null ? "" : cst.getCountryRegionCity());
r.createCell(ci++).setCellValue(cst.getPhoneNumber() == null ? "" : cst.getPhoneNumber());
r.createCell(ci++).setCellValue(cst.getDesignTitle() == null ? "" : cst.getDesignTitle());
r.createCell(ci++).setCellValue(cst.getDesignDescription() == null ? "" : cst.getDesignDescription());
// r.createCell(ci++).setCellValue(cst.getPdfPath() == null ? "" : cst.getPdfPath());
// r.createCell(ci++).setCellValue(cst.getVideoPath() == null ? "" : cst.getVideoPath());
// 视频时长(秒)
r.createCell(ci++).setCellValue(cst.getVideoDuration() == null ? "" : cst.getVideoDuration().toString());
// 视频大小、PDF 大小:以 MB 导出,保留两位小数
if (cst.getVideoSize() == null) {
r.createCell(ci++).setCellValue("");
} else {
double vMb = cst.getVideoSize() / 1024.0 / 1024.0;
r.createCell(ci++).setCellValue(String.format("%.2f", vMb));
}
if (cst.getPdfSize() == null) {
r.createCell(ci++).setCellValue("");
} else {
double pMb = cst.getPdfSize() / 1024.0 / 1024.0;
r.createCell(ci++).setCellValue(String.format("%.2f", pMb));
}
r.createCell(ci++).setCellValue(cst.getCreatedAt() == null ? "" : cst.getCreatedAt().toString());
r.createCell(ci++).setCellValue(cst.getUpdatedAt() == null ? "" : cst.getUpdatedAt().toString());
}
workbook.write(fos);
fos.flush();
log.info("Exported contestants to local file: {}", outPath.toString());
} catch (IOException e) {
log.error("save contestants to local failed", e);
throw e;
}
}
@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());
dto.setVideoDuration(existing.getVideoDuration());
dto.setPdfSize(existing.getPdfSize());
dto.setVideoSize(existing.getVideoSize());
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);
}
@Override
public int exportContestantFiles(Integer minContestantNumber, Integer maxContestantNumber) throws Exception {
if (minContestantNumber == null || maxContestantNumber == null) {
throw new BusinessException("minContestantNumber and maxContestantNumber are required.");
}
if (minContestantNumber > maxContestantNumber) {
throw new BusinessException("minContestantNumber cannot be greater than maxContestantNumber.");
}
// 1. 根据contestantNumber范围查询参赛者
QueryWrapper<Contestant> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.ge(Contestant::getContestantNumber, minContestantNumber)
.le(Contestant::getContestantNumber, maxContestantNumber)
.orderByAsc(Contestant::getContestantNumber);
List<Contestant> contestants = contestantMapper.selectList(queryWrapper);
if (contestants.isEmpty()) {
log.info("No contestants found in range [{}, {}]", minContestantNumber, maxContestantNumber);
return 0;
}
// 2. 创建基础目录
String baseDir = uploadDir + "/contestants";
Path basePath = Paths.get(baseDir).toAbsolutePath();
Files.createDirectories(basePath);
log.info("Base directory created: {}", basePath);
int exportedCount = 0;
// 3. 遍历每个参赛者,下载文件
for (Contestant contestant : contestants) {
Integer contestantNumber = contestant.getContestantNumber();
if (contestantNumber == null) {
log.warn("Contestant {} has no contestantNumber, skipping", contestant.getId());
continue;
}
// 创建参赛者文件夹
String contestantDir = baseDir + "/" + contestantNumber;
Path contestantPath = Paths.get(contestantDir);
Files.createDirectories(contestantPath);
// 下载PDF文件
String pdfPath = contestant.getPdfPath();
if (StringUtils.isNotBlank(pdfPath)) {
try {
String fileName = pdfPath.contains("/") ?
pdfPath.substring(pdfPath.lastIndexOf("/") + 1) : "design.pdf";
downloadFileFromMinio(pdfPath, contestantPath.toString(), "design.pdf");
log.info("Downloaded PDF for contestant {}", fileName);
} catch (Exception e) {
log.error("Failed to download PDF for contestant {}: {}", contestantNumber, e.getMessage());
}
}
// 下载视频文件
String videoPath = contestant.getVideoPath();
if (StringUtils.isNotBlank(videoPath)) {
try {
// 根据路径判断视频格式
String fileName = videoPath.contains("/") ?
videoPath.substring(videoPath.lastIndexOf("/") + 1) : "video.mp4";
downloadFileFromMinio(videoPath, contestantPath.toString(), fileName);
log.info("Downloaded video for contestant {}", contestantNumber);
} catch (Exception e) {
log.error("Failed to download video for contestant {}: {}", contestantNumber, e.getMessage());
}
}
exportedCount++;
}
log.info("Exported {} contestants' files to {}", exportedCount, basePath);
return exportedCount;
}
/**
* 从MinIO下载文件到本地
* @param minioPath MinIO路径 (格式: bucketName/objectPath)
* @param localDir 本地目录
* @param fileName 本地文件名
*/
private void downloadFileFromMinio(String minioPath, String localDir, String fileName) {
if (StringUtils.isBlank(minioPath)) {
return;
}
// 从路径中提取bucket名称和对象名称
int index = minioPath.indexOf("/");
if (index == -1) {
log.warn("Invalid MinIO path: {}", minioPath);
return;
}
String bucketName = minioPath.substring(0, index);
String objectName = minioPath.substring(index + 1);
// 构建本地文件完整路径
Path localFilePath = Paths.get(localDir, fileName);
// 下载文件
minioUtil.downloadMinioObjectToLocal(bucketName, objectName, localFilePath.toString());
}
}

View File

@@ -28,6 +28,7 @@ import io.netty.util.internal.StringUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@@ -53,7 +54,11 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
private final PythonTAllInfoService pythonTAllInfoService; private final PythonTAllInfoService pythonTAllInfoService;
@Override @Override
@Transactional(rollbackFor = Exception.class)
public LibraryModelPointVO saveOrEditTemplatePoint(LibraryModelPointDTO libraryModelPointDTO) { public LibraryModelPointVO saveOrEditTemplatePoint(LibraryModelPointDTO libraryModelPointDTO) {
// 参数校验
validateInputParams(libraryModelPointDTO);
LibraryModelPointVO libraryModelPointVO = CopyUtil.copyObject(libraryModelPointDTO, LibraryModelPointVO.class); LibraryModelPointVO libraryModelPointVO = CopyUtil.copyObject(libraryModelPointDTO, LibraryModelPointVO.class);
// 不管是保存还是另存为都需要传模特的libraryId // 不管是保存还是另存为都需要传模特的libraryId
@@ -71,7 +76,8 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
// 更新模特图片 // 更新模特图片
if (flag) { if (flag) {
libModel.setUrl(url); libModel.setUrl(url);
libModel.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false)); String preSignedUrl = minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
libModel.setMd5(MD5Utils.encryptFile(preSignedUrl, false));
List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(url); List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(url);
libModel.setWidth(imagesWidthAndHeight.get(0)); libModel.setWidth(imagesWidthAndHeight.get(0));
libModel.setHigh(imagesWidthAndHeight.get(1)); libModel.setHigh(imagesWidthAndHeight.get(1));
@@ -104,25 +110,10 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
} else { } else {
// 不覆盖,即另存为 // 不覆盖,即另存为
// 新增模特library信息 Library saveAsModel = createNewLibraryCopy(libModel, libraryModelPointDTO);
Library saveAsModel = new Library();
saveAsModel.setAccountId(libModel.getAccountId());
saveAsModel.setLevel1Type(libModel.getLevel1Type());
saveAsModel.setLevel2Type(libModel.getLevel2Type());
String ageGroup = StringUtil.isNullOrEmpty(libModel.getLevel3Type()) ? "Adult" : libModel.getLevel3Type();
saveAsModel.setLevel3Type(ageGroup);
saveAsModel.setName(DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD));
saveAsModel.setUrl(url);
saveAsModel.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false));
List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(url);
saveAsModel.setWidth(imagesWidthAndHeight.get(0));
saveAsModel.setHigh(imagesWidthAndHeight.get(1));
saveAsModel.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone()));
libraryService.save(saveAsModel);
// 更新新的模特在library中的id,用于后面新建模特点位信息用
libraryModelPointDTO.setLibraryId(saveAsModel.getId());
// 新增模特点位信息 // 新增模特点位信息
libraryModelPointDTO.setLibraryId(saveAsModel.getId()); // 更新libraryId为新创建的模型ID
LibraryModelPoint libraryModelPoint = resolvePoint(libraryModelPointDTO); LibraryModelPoint libraryModelPoint = resolvePoint(libraryModelPointDTO);
libraryModelPoint.setModelType("Library"); libraryModelPoint.setModelType("Library");
libraryModelPoint.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone())); libraryModelPoint.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone()));
@@ -130,22 +121,50 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
libraryModelPointVO.setTemplateId(libraryModelPoint.getId()); libraryModelPointVO.setTemplateId(libraryModelPoint.getId());
libraryModelPointVO.setRelationId(libraryModelPoint.getRelationId()); libraryModelPointVO.setRelationId(libraryModelPoint.getRelationId());
} }
//编辑
/*if (!StringUtils.isEmpty(libraryModelPointDTO.getModelSex())) {
Library byId = libraryService.getById(libraryModelPointDTO.getLibraryId());
if (!byId.getLevel2Type().equals(libraryModelPointDTO.getModelSex())) {
if (byId.getLevel2Type().equals(Sex.FEMALE.getValue())) {
libraryService.checkModel(Sex.FEMALE.getValue(), Collections.singletonList(byId.getId()), 1);
}else {
libraryService.checkModel(Sex.MALE.getValue(), Collections.singletonList(byId.getId()), 1);
}
byId.setLevel2Type(libraryModelPointDTO.getModelSex());
libraryService.updateById(byId);
}
}*/
return libraryModelPointVO; return libraryModelPointVO;
} }
/**
* 验证输入参数
*/
private void validateInputParams(LibraryModelPointDTO libraryModelPointDTO) {
if (libraryModelPointDTO == null) {
throw new BusinessException("libraryModelPointDTO cannot be null");
}
if (libraryModelPointDTO.getLibraryId() == null || libraryModelPointDTO.getLibraryId() <= 0) {
throw new BusinessException("libraryId is required");
}
if (StringUtils.isEmpty(libraryModelPointDTO.getModelPath())) {
throw new BusinessException("modelPath is required");
}
if (StringUtils.isEmpty(libraryModelPointDTO.getTimeZone())) {
throw new BusinessException("timeZone is required");
}
}
/**
* 创建新的库模型副本
*/
private Library createNewLibraryCopy(Library originalModel, LibraryModelPointDTO libraryModelPointDTO) {
// 新增模特library信息
Library saveAsModel = new Library();
saveAsModel.setAccountId(originalModel.getAccountId());
saveAsModel.setLevel1Type(originalModel.getLevel1Type());
saveAsModel.setLevel2Type(originalModel.getLevel2Type());
String ageGroup = StringUtil.isNullOrEmpty(originalModel.getLevel3Type()) ? "Adult" : originalModel.getLevel3Type();
saveAsModel.setLevel3Type(ageGroup);
saveAsModel.setName(DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD));
saveAsModel.setUrl(libraryModelPointDTO.getModelPath());
String preSignedUrl = minioUtil.getPreSignedUrl(libraryModelPointDTO.getModelPath(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
saveAsModel.setMd5(MD5Utils.encryptFile(preSignedUrl, false));
List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(libraryModelPointDTO.getModelPath());
saveAsModel.setWidth(imagesWidthAndHeight.get(0));
saveAsModel.setHigh(imagesWidthAndHeight.get(1));
saveAsModel.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone()));
libraryService.save(saveAsModel);
return saveAsModel;
}
@Override @Override
public LibraryModelPointVO saveOrEditTemplatePointOld(LibraryModelPointDTO libraryModelPointDTO) { public LibraryModelPointVO saveOrEditTemplatePointOld(LibraryModelPointDTO libraryModelPointDTO) {
// Library library = libraryService.getById(libraryModelPointDTO.getLibraryId()); // Library library = libraryService.getById(libraryModelPointDTO.getLibraryId());

View File

@@ -79,6 +79,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
throw new BusinessException("type.cannot.be.empty"); throw new BusinessException("type.cannot.be.empty");
} }
Long accountId = UserContext.getUserHolder().getId(); Long accountId = UserContext.getUserHolder().getId();
Account account = accountService.getById(accountId);
// 查动态 // 查动态
if (!StringUtils.isNullOrEmpty(getNotificationDTO.getType()) && getNotificationDTO.getType().equals("newPosted")) { if (!StringUtils.isNullOrEmpty(getNotificationDTO.getType()) && getNotificationDTO.getType().equals("newPosted")) {
return getNewPosted(accountId, getNotificationDTO.getPage(), getNotificationDTO.getSize()); return getNewPosted(accountId, getNotificationDTO.getPage(), getNotificationDTO.getSize());
@@ -92,6 +93,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
if (getNotificationDTO.getType().equals("system")) { if (getNotificationDTO.getType().equals("system")) {
queryWrapper.lambda().eq(Notification::getType, "system") queryWrapper.lambda().eq(Notification::getType, "system")
.gt(Notification::getCreateTime, account.getCreateDate())
.and(wrapper -> wrapper .and(wrapper -> wrapper
.isNull(Notification::getReceiverId) .isNull(Notification::getReceiverId)
.or() .or()
@@ -103,7 +105,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
Page<Notification> notificationPage = baseMapper.selectPage(new Page<>(getNotificationDTO.getPage(), getNotificationDTO.getSize()), queryWrapper); Page<Notification> notificationPage = baseMapper.selectPage(new Page<>(getNotificationDTO.getPage(), getNotificationDTO.getSize()), queryWrapper);
List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId); List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId, account.getCreateDate());
IPage<NotificationVO> convert = notificationPage.convert(o -> { IPage<NotificationVO> convert = notificationPage.convert(o -> {
NotificationVO notificationVO = CopyUtil.copyObject(o, NotificationVO.class); NotificationVO notificationVO = CopyUtil.copyObject(o, NotificationVO.class);
Account senderAccount = accountService.getById(notificationVO.getSenderId()); Account senderAccount = accountService.getById(notificationVO.getSenderId());
@@ -192,6 +194,8 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
if (!type.equals("system")) { if (!type.equals("system")) {
// 个人未读消息 // 个人未读消息
count = getUnreadCountByType(type, receiverId); count = getUnreadCountByType(type, receiverId);
} else if (Objects.isNull(receiverId)) {
count = 1L;
} else { } else {
// 系统未读消息 // 系统未读消息
count = getUnreadSystemNotification(receiverId); count = getUnreadSystemNotification(receiverId);
@@ -247,6 +251,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
private Long getUnreadSystemNotification(Long receiverId) { private Long getUnreadSystemNotification(Long receiverId) {
// Long accountId = UserContext.getUserHolder().getId(); // Long accountId = UserContext.getUserHolder().getId();
Account account = accountService.getById(receiverId);
// 计算总的系统通知数量 // 计算总的系统通知数量
QueryWrapper<Notification> queryWrapper = new QueryWrapper<>(); QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(Notification::getType, "system") queryWrapper.lambda().eq(Notification::getType, "system")
@@ -255,6 +260,9 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
.or() .or()
.eq(Notification::getReceiverId, receiverId) .eq(Notification::getReceiverId, receiverId)
); );
if (Objects.nonNull(account)) {
queryWrapper.lambda().gt(Notification::getCreateTime, account.getCreateDate());
}
Long totalSysCount = baseMapper.selectCount(queryWrapper); Long totalSysCount = baseMapper.selectCount(queryWrapper);
// 计算单个用户读了多少条系统数据 // 计算单个用户读了多少条系统数据
@@ -302,6 +310,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
// 一键已读 // 一键已读
public void setReadAll(String type) { public void setReadAll(String type) {
Long accountId = UserContext.getUserHolder().getId(); Long accountId = UserContext.getUserHolder().getId();
Account account = accountService.getById(accountId);
// 指定某个用户的某种类型的数据,将未读数据全部已读 // 指定某个用户的某种类型的数据,将未读数据全部已读
if (!type.equals("system")) { if (!type.equals("system")) {
// 个人消息已读 // 个人消息已读
@@ -309,7 +318,7 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
} else { } else {
// 系统消息已读 // 系统消息已读
// 1、先确定当前用户未读的系统消息有哪些 // 1、先确定当前用户未读的系统消息有哪些
List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId); List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId, account.getCreateDate());
// 2、将未读的设为已读 // 2、将未读的设为已读
if (!unreadSysNotificationIds.isEmpty()) setReadStatusSystem(unreadSysNotificationIds); if (!unreadSysNotificationIds.isEmpty()) setReadStatusSystem(unreadSysNotificationIds);
} }

View File

@@ -258,6 +258,11 @@ public class PanToneServiceImpl extends ServiceImpl<PanToneMapper, PanTone> impl
d.setH(getRgbByHsvBatchDTO.getH()); d.setH(getRgbByHsvBatchDTO.getH());
d.setS(getRgbByHsvBatchDTO.getS()); d.setS(getRgbByHsvBatchDTO.getS());
d.setV(getRgbByHsvBatchDTO.getV()); 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( Map<Integer, PantoneVO> valueToPantoneVo = templateResposne.stream().collect(Collectors.toMap(

View File

@@ -31,6 +31,11 @@ public class RabbitMQServiceImpl implements RabbitMQService {
mqPublisher.sendGenerateMessage(message); mqPublisher.sendGenerateMessage(message);
} }
@Override
public void publishMessageToGenerateResult(String message) {
mqPublisher.sendGenerateResultMessage(message);
}
@Override @Override
public void publishMessageToSR(String message) { public void publishMessageToSR(String message) {
mqPublisher.sendSRMessage(message); mqPublisher.sendSRMessage(message);

View File

@@ -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.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -28,6 +29,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -37,9 +39,11 @@ import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException; import java.time.format.DateTimeParseException;
import java.util.Collections; import java.util.*;
import java.util.List;
import java.util.Objects; 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;
@Slf4j @Slf4j
@Service @Service
@@ -62,7 +66,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
SubscriptionPlan subscriptionPlan = CopyUtil.copyObject(subscriptionPlanDTO, SubscriptionPlan.class); SubscriptionPlan subscriptionPlan = CopyUtil.copyObject(subscriptionPlanDTO, SubscriptionPlan.class);
if (StringUtils.isBlank(subscriptionPlanDTO.getStatus())) { if (StringUtils.isBlank(subscriptionPlanDTO.getStatus())) {
subscriptionPlan.setStatus(SubscriptionPlan.SubscriptionStatus.PENDING.name()); subscriptionPlan.setStatus(PENDING.name());
} }
if (Objects.isNull(subscriptionPlan.getName())) { if (Objects.isNull(subscriptionPlan.getName())) {
subscriptionPlan.setName("DEFAULT_NAME"); subscriptionPlan.setName("DEFAULT_NAME");
@@ -74,6 +78,10 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
} }
baseMapper.insert(subscriptionPlan); baseMapper.insert(subscriptionPlan);
if (subscriptionPlan.getStatus().equals(SubscriptionPlan.SubscriptionStatus.ACTIVE.name())) {
// 执行一次激活扫描器
activeSubscriptionPlan(subscriptionPlan.getId());
}
} }
private void validateCreatePlanParams(SubscriptionPlanDTO subscriptionPlanDTO) { private void validateCreatePlanParams(SubscriptionPlanDTO subscriptionPlanDTO) {
@@ -93,52 +101,230 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
if (Objects.isNull(account)) { if (Objects.isNull(account)) {
throw new BusinessException("unknown.administrator.user"); throw new BusinessException("unknown.administrator.user");
} }
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 @Override
public void updatePlan(UpdateSubscriptionPlanDTO updateDTO) { public void updatePlan(UpdateSubscriptionPlanDTO dto) {
if (Objects.isNull(updateDTO.getId())) { if (dto.getId() == null) {
throw new BusinessException("id.cannot.be.empty"); throw new BusinessException("id.cannot.be.empty");
} }
SubscriptionPlan subscriptionPlan = baseMapper.selectById(updateDTO.getId()); SubscriptionPlan plan = baseMapper.selectById(dto.getId());
if (Objects.isNull(subscriptionPlan)) { if (plan == null) {
throw new BusinessException("unknown.subscription.plan"); throw new BusinessException("unknown.subscription.plan");
} }
if (Objects.nonNull(updateDTO.getCurrentPeriodStart()) && !updateDTO.getCurrentPeriodStart().equals(subscriptionPlan.getCurrentPeriodStart())) { boolean activateToday = false;
subscriptionPlan.setCurrentPeriodStart(updateDTO.getCurrentPeriodStart());
activateToday = handlePeriodStart(dto, plan);
handlePeriodEnd(dto, plan);
handleAccountNum(dto, plan);
handleCreditLimit(dto, plan);
handleBasicInfo(dto, plan);
plan.setUpdateTime(LocalDateTime.now());
updateById(plan);
postUpdateProcess(plan, activateToday);
} }
if (Objects.nonNull(updateDTO.getCurrentPeriodEnd()) && !updateDTO.getCurrentPeriodEnd().equals(subscriptionPlan.getCurrentPeriodEnd())) { // ===================== 字段处理 =====================
subscriptionPlan.setCurrentPeriodEnd(updateDTO.getCurrentPeriodEnd());
/**
* 处理开始时间,返回是否需要当天激活
*/
private boolean handlePeriodStart(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
Long newStart = dto.getCurrentPeriodStart();
if (newStart == null || newStart.equals(plan.getCurrentPeriodStart())) {
return false;
} }
if (Objects.nonNull(updateDTO.getAccountNum()) && !updateDTO.getAccountNum().equals(subscriptionPlan.getAccountNum())) { if (ACTIVE.name().equals(plan.getStatus())) {
subscriptionPlan.setAccountNum(updateDTO.getAccountNum()); throw new BusinessException(
"only.subscription.plans.with.a.PENDING.status.can.have.their.start.time.modified"
);
} }
if (Objects.nonNull(updateDTO.getCreditLimit()) && !updateDTO.getCreditLimit().equals(subscriptionPlan.getCreditLimit())) { plan.setCurrentPeriodStart(newStart);
subscriptionPlan.setCreditLimit(updateDTO.getCreditLimit()); return isToday(newStart);
} }
if (Objects.nonNull(updateDTO.getAdminAccId()) && !updateDTO.getAdminAccId().equals(subscriptionPlan.getAdminAccId())) { /**
subscriptionPlan.setAdminAccId(updateDTO.getAdminAccId()); * 处理结束时间(只能延长)
*/
private void handlePeriodEnd(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
Long newEnd = dto.getCurrentPeriodEnd();
if (newEnd == null || newEnd.equals(plan.getCurrentPeriodEnd())) {
return;
} }
if (StringUtils.isNotBlank(updateDTO.getName()) && !updateDTO.getName().equals(subscriptionPlan.getName())) { if (newEnd < plan.getCurrentPeriodEnd()) {
subscriptionPlan.setName(updateDTO.getName()); throw new BusinessException(
"the.subscription.end.date.can.be.extended.only.not.reduced"
);
} }
subscriptionPlan.setUpdateTime(LocalDateTime.now()); plan.setCurrentPeriodEnd(newEnd);
updateById(subscriptionPlan);
} }
public void updatePlan() { /**
* 处理账号数量
*/
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 @Override
@@ -156,6 +342,12 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
queryWrapper.lambda().in(SubscriptionPlan::getStatus, subscriptionPlanPageQuery.getStatus()); 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); return baseMapper.selectList(queryWrapper);
} }
@@ -210,6 +402,9 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
wrapper.in("sp.status", query.getStatus()); 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) wrapper.ne("sp.is_deleted", 1)
.orderByDesc("sp.create_time"); .orderByDesc("sp.create_time");
@@ -387,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"); throw new BusinessException("valid.subscription.period");
} }
} }
@@ -407,11 +602,19 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
return currentPeriodEnd < currentTimestamp; return currentPeriodEnd < currentTimestamp;
} }
public void activeSubscriptionPlan() { public void activeSubscriptionPlan(Long planId) {
log.info("开始执行订阅计划生效检查..."); log.info("开始执行订阅计划生效检查...");
// 支持按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找出今天开始生效的计划 // 1. 扫描所有的订阅计划的开始时间currentPeriodStart找出今天开始生效的计划
List<SubscriptionPlan> todayActivePlans = findTodayActivePlans(); todayActivePlans = findTodayActivePlans();
if (CollectionUtils.isEmpty(todayActivePlans)) { if (CollectionUtils.isEmpty(todayActivePlans)) {
log.info("今日没有需要生效的订阅计划"); log.info("今日没有需要生效的订阅计划");
@@ -419,6 +622,8 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
} }
log.info("发现{}个今日生效的订阅计划", todayActivePlans.size()); log.info("发现{}个今日生效的订阅计划", todayActivePlans.size());
}
// 2. 处理每个今天开始生效的订阅计划 // 2. 处理每个今天开始生效的订阅计划
for (SubscriptionPlan plan : todayActivePlans) { for (SubscriptionPlan plan : todayActivePlans) {
@@ -450,6 +655,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
// 查询今天开始生效的订阅计划 // 查询今天开始生效的订阅计划
QueryWrapper<SubscriptionPlan> queryWrapper = new QueryWrapper<>(); QueryWrapper<SubscriptionPlan> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("is_deleted", 0) // 未删除 queryWrapper.eq("is_deleted", 0) // 未删除
.in("status", Arrays.asList(PENDING.name(), ACTIVE.name())) // 还未被激活的,或者设置为激活状态但是未被实际激活的
.between("current_period_start", todayStart, todayEnd) // 今天开始生效 .between("current_period_start", todayStart, todayEnd) // 今天开始生效
.orderByAsc("current_period_start"); // 按开始时间排序 .orderByAsc("current_period_start"); // 按开始时间排序
@@ -487,7 +693,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
*/ */
private void updateAccount(Account account, SubscriptionPlan plan, boolean isAdmin) { private void updateAccount(Account account, SubscriptionPlan plan, boolean isAdmin) {
// 如果是管理员的切换,先再次记录一下已使用的积分 // 如果是管理员的切换,先再次记录一下已使用的积分
if (isAdmin) { if (isAdmin && Objects.nonNull(account.getSubscriptionPlanId())) {
SubscriptionPlan currentPlan = baseMapper.selectById(account.getSubscriptionPlanId()); SubscriptionPlan currentPlan = baseMapper.selectById(account.getSubscriptionPlanId());
if (currentPlan.getCreditUsage().compareTo(account.getCreditsUsage()) < 0) { if (currentPlan.getCreditUsage().compareTo(account.getCreditsUsage()) < 0) {
updateSubscriptionPlanUsage(currentPlan, account.getCreditsUsage()); updateSubscriptionPlanUsage(currentPlan, account.getCreditsUsage());

View File

@@ -9,6 +9,7 @@ import com.ai.da.model.vo.DesignPythonOutfitVO;
import com.ai.da.model.vo.TDesignPythonOutfitDetailVO; import com.ai.da.model.vo.TDesignPythonOutfitDetailVO;
import com.ai.da.service.ITDesignPythonOutfitDetailService; import com.ai.da.service.ITDesignPythonOutfitDetailService;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -67,6 +68,13 @@ public class TDesignPythonOutfitDetailServiceImpl extends ServiceImpl<TDesignPyt
designPythonOutfitVO.setScale(modifyScale(detail.getScale())); designPythonOutfitVO.setScale(modifyScale(detail.getScale()));
designPythonOutfitVO.setOffset(StringUtil.isNullOrEmpty(detail.getOffset()) ? Arrays.asList(0L, 0L) : parseLongList(detail.getOffset())); designPythonOutfitVO.setOffset(StringUtil.isNullOrEmpty(detail.getOffset()) ? Arrays.asList(0L, 0L) : parseLongList(detail.getOffset()));
designPythonOutfitVO.setPriority(Math.abs(detail.getPriority())); 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); // designPythonOutfitVO.setOffset(CollectionUtil.isEmpty(offset) ? Arrays.asList(0L, 0L) : offset);
/*if (!StringUtil.isNullOrEmpty(detail.getImageSize())){ /*if (!StringUtil.isNullOrEmpty(detail.getImageSize())){

View File

@@ -1469,15 +1469,10 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
sortRank(toProductImageResult); sortRank(toProductImageResult);
results.add(new MagicToolResultVO(taskId, fluxResult)); results.add(new MagicToolResultVO(taskId, fluxResult));
} else { } else {
fluxResult="Fail"; results.add(processFluxResult(fluxResult, toProductImageResult, taskId, toProductImageRecord.getPrompt()));
toProductImageResult.setStatus(fluxResult); // 扣积分
toProductImageResultMapper.updateById(toProductImageResult); Boolean flag = creditsService.taskCreditsDeduction(project.getAccountId(), taskId);
sortRank(toProductImageResult); if (flag) creditsService.updateChangedCredits(String.valueOf(project.getAccountId()), taskId);
results.add(new MagicToolResultVO(taskId, fluxResult));
// results.add(processFluxResult(fluxResult, toProductImageResult, taskId, toProductImageRecord.getPrompt()));
// // 扣积分
// Boolean flag = creditsService.taskCreditsDeduction(project.getAccountId(), taskId);
// if (flag) creditsService.updateChangedCredits(String.valueOf(project.getAccountId()), taskId);
} }
// 将积分暂扣区的积分移除 // 将积分暂扣区的积分移除
if (toProductImageResult.getStatus().equals("Fail")) { if (toProductImageResult.getStatus().equals("Fail")) {
@@ -1534,11 +1529,12 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
if (!collectionSorts.isEmpty()) { if (!collectionSorts.isEmpty()) {
collectionSortMapper.deleteById(collectionSorts.get(0)); collectionSortMapper.deleteById(collectionSorts.get(0));
List<CollectionSort> collectionSorts1 = collectionSortMapper.selectList(new LambdaQueryWrapper<CollectionSort>().eq(CollectionSort::getParentId, collectionSorts.get(0).getParentId()).orderByDesc(CollectionSort::getSort).last("LIMIT 1")); List<CollectionSort> collectionSorts1 = collectionSortMapper.selectList(new LambdaQueryWrapper<CollectionSort>().eq(CollectionSort::getParentId, collectionSorts.get(0).getParentId()).gt(CollectionSort::getSort, collectionSorts.get(0).getSort()));
if (!collectionSorts1.isEmpty()) { if (!collectionSorts1.isEmpty()) {
collectionSorts1.get(0).setSort(collectionSorts1.get(0).getSort() - 1); for (CollectionSort collectionSort : collectionSorts1) {
collectionSortMapper.updateById(collectionSorts1.get(0)); collectionSort.setSort(collectionSort.getSort() - 1);
collectionSortMapper.updateById(collectionSort);
}
} }
} }
} }

View 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();
}

View 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
}
}

View File

@@ -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";
}
}
}

View File

@@ -22,7 +22,7 @@ spring.security.jwtExpiration=8640000000
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\ 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/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/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 spring.security.authApi=/auth/login
@@ -63,6 +63,7 @@ minio.bucketName.gradient=aida-gradient
minio.bucketName.modifiedSketch=aida-modified-sketch minio.bucketName.modifiedSketch=aida-modified-sketch
minio.bucketName.slogan=aida-slogan minio.bucketName.slogan=aida-slogan
minio.bucketName.partialDesign=aida-partial-design minio.bucketName.partialDesign=aida-partial-design
minio.bucketName.globalAward=global-award
redirect_url=http://18.167.251.121:7788 redirect_url=http://18.167.251.121:7788
spring.rabbitmq.host=18.167.251.121 spring.rabbitmq.host=18.167.251.121
@@ -124,9 +125,9 @@ orderList.link=https://develop.aida.com.hk/home/homePage?order=
# 0 不发送邮件通知 1 发送邮件通知 # 0 不发送邮件通知 1 发送邮件通知
stripe.webhook.fail.reminder=0 stripe.webhook.fail.reminder=0
# kim test # kim test
stripe.paymentMethodConfiguration=pmc_1LywTWH7nPZ8bkrN6FvdCUWG #stripe.paymentMethodConfiguration=pmc_1LywTWH7nPZ8bkrN6FvdCUWG
# developer test # developer test
#stripe.paymentMethodConfiguration=pmc_1QIKyq02n1TEydyNKVEYvhW7 stripe.paymentMethodConfiguration=pmc_1QIKyq02n1TEydyNKVEYvhW7
#thymelea模板配置 #thymelea模板配置
#控制 Thymeleaf 是否启用模板缓存 生产环境用true,以提高性能 #控制 Thymeleaf 是否启用模板缓存 生产环境用true,以提高性能
spring.thymeleaf.cache=false spring.thymeleaf.cache=false
@@ -159,3 +160,25 @@ 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 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://aida-global-design-awards.com.hk/contestants?id=

View File

@@ -22,7 +22,7 @@ spring.security.jwtExpiration=604800000
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\ 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/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/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/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**, /api/subscription_plan/**,/api/global-award/**
spring.security.authApi=/auth/login spring.security.authApi=/auth/login
@@ -63,6 +63,7 @@ minio.bucketName.gradient=aida-gradient
minio.bucketName.modifiedSketch=aida-modified-sketch minio.bucketName.modifiedSketch=aida-modified-sketch
minio.bucketName.slogan=aida-slogan minio.bucketName.slogan=aida-slogan
minio.bucketName.partialDesign=aida-partial-design minio.bucketName.partialDesign=aida-partial-design
minio.bucketName.globalAward=global-award
redirect_url=http://18.167.251.121:7788 redirect_url=http://18.167.251.121:7788
spring.rabbitmq.host=18.167.251.121 spring.rabbitmq.host=18.167.251.121
@@ -71,15 +72,15 @@ spring.rabbitmq.username=rabbit
spring.rabbitmq.password=123456 spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/ spring.rabbitmq.virtual-host=/
spring.redis.host=172.31.11.32 spring.data.redis.host=172.31.11.32
#spring.redis.host=18.167.251.121 #spring.data.redis.host=18.167.251.121
spring.redis.port=6379 spring.data.redis.port=6379
spring.redis.database=2 spring.data.redis.database=2
spring.redis.password=Aidlab spring.data.redis.password=Aidlab
spring.redis.lettuce.pool.max-active=8 spring.data.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-idle=8 spring.data.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0 spring.data.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-wait=5 spring.data.redis.lettuce.pool.max-wait=5
redis.key.orderForGenerate=OrderForGenerate redis.key.orderForGenerate=OrderForGenerate
redis.key.generateCancelSet=GenerateCancelSet redis.key.generateCancelSet=GenerateCancelSet
@@ -157,3 +158,25 @@ google.client.id=29310152396-nnsd3h533fld665oguu8ovrt1nukmt46.apps.googleusercon
google.client.secret=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 google.client.secret=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
google.redirect.uri=https://www.api.aida.com.hk/api/third/party/auth/google_callback 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://aida-global-design-awards.com.hk/contestants?id=

View File

@@ -61,10 +61,10 @@
</if> </if>
<!-- 添加时间区间查询条件 --> <!-- 添加时间区间查询条件 -->
<if test="startTime != null and startTime != ''"> <if test="startTime != null and startTime != ''">
AND cd.create_time &gt;= #{startTime} AND create_time &gt;= #{startTime}
</if> </if>
<if test="endTime != null and endTime != ''"> <if test="endTime != null and endTime != ''">
AND cd.create_time &lt;= #{endTime} AND create_time &lt;= #{endTime}
</if> </if>
GROUP BY GROUP BY
account_id account_id

View File

@@ -52,8 +52,10 @@
AND b.create_date between #{startTime} and #{endTime} AND b.create_date between #{startTime} and #{endTime}
</if> </if>
</where> </where>
<if test="filterBySecond">
and b.create_date not like '%:01' and b.create_date not like '%:01'
and b.create_date not like '%:02' and b.create_date not like '%:02'
</if>
<if test="ids != null and ids.size() > 0"> <if test="ids != null and ids.size() > 0">
and a.id in and a.id in
<foreach item="id" collection="ids" open="(" separator="," close=")"> <foreach item="id" collection="ids" open="(" separator="," close=")">

View File

@@ -61,6 +61,7 @@
FROM `t_sys_notification_read_status` FROM `t_sys_notification_read_status`
WHERE account_id = #{accountId} WHERE account_id = #{accountId}
) )
AND create_time > #{createTime}
</select> </select>

View File

@@ -0,0 +1,21 @@
<?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>

View File

@@ -208,6 +208,13 @@ page.num.limit=The page number must be greater than 0.
end.time.must.be.later.than.the.start.time=The subscription end time must be later than the start time. end.time.must.be.later.than.the.start.time=The subscription end time must be later than the start time.
please.specify.the.organizationId=Please specify the organizationId. 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. 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: # Informative:

View File

@@ -204,6 +204,13 @@ page.num.limit=页码必须大于0
end.time.must.be.later.than.the.start.time=订阅结束时间必须晚于开始时间 end.time.must.be.later.than.the.start.time=订阅结束时间必须晚于开始时间
please.specify.the.organizationId=请指定organizationId please.specify.the.organizationId=请指定organizationId
switch.failed.sub-account.not.under.your.active.subscription=切换失败,该子账号不属于您当前管理的订阅计划 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: # Informative:

View File

@@ -27,20 +27,20 @@ paypal.webhook_id=1D107312EX592781K
##### Stripe ##### Stripe
# developer # developer
#stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2 stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2
# dev 端点 # dev 端点
#stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w
# local 端点 # local 端点
#stripe.webhook-sign-secret=whsec_TJcMSnAkh4uktrNY1M6Iy8XaVze4Rzqm #stripe.webhook-sign-secret=whsec_TJcMSnAkh4uktrNY1M6Iy8XaVze4Rzqm
# kim - test # kim - test
stripe.private-key=sk_test_51LwPrxH7nPZ8bkrNj67TFD7sxucaTANs1lf0KGSu1QSJfxYXcnigq2wTaZyZzST7y0fMbhhvaJZ4LjjFhr95M83a00eXrmOTL0 #stripe.private-key=sk_test_51LwPrxH7nPZ8bkrNj67TFD7sxucaTANs1lf0KGSu1QSJfxYXcnigq2wTaZyZzST7y0fMbhhvaJZ4LjjFhr95M83a00eXrmOTL0
# prod 端点 # prod 端点
#stripe.webhook-sign-secret=whsec_GoyVEAaBtuGD5Rt55z83JnPnLDAZTN3u #stripe.webhook-sign-secret=whsec_GoyVEAaBtuGD5Rt55z83JnPnLDAZTN3u
# local 端点 # local 端点
#stripe.webhook-sign-secret=whsec_NvwM3hDQiN5GXclYOYekE9IKHLjmROF8 #stripe.webhook-sign-secret=whsec_NvwM3hDQiN5GXclYOYekE9IKHLjmROF8
# dev 端点 # dev 端点
stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL #stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL
# kim - live # kim - live
#stripe.private-key=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 #stripe.private-key=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