34 Commits

Author SHA1 Message Date
daf84cd6fe feat(ai): 增加AI模型样本管理功能
- 实现模型引用样本和获取关联样本功能
- 添加模型准确度分析和差异筛查功能
- 实现模型迭代复制机制
- 重构样本控制器支持批量更新和模型关联
- 优化样本响应VO结构增加标签和模型信息
- 完善模型和样本的数据访问层实现
- 集成翻译服务器进行模型分析验证
- 优化文件上传和下载功能实现
2026-03-05 18:32:03 +08:00
a8aa2a60fe feat(ai): 添加样本文件后缀支持和标签分组功能
- 在AiSampleDO中新增suffix字段存储文件后缀
- 在AiSampleRespVO中添加suffix响应字段
- 修改文件上传逻辑分离文件名和后缀并分别存储
- 新增根据标签ID获取分组列表的API接口
- 在标签服务中实现分组关联查询功能
- 修复标签分组关联关系创建和更新逻辑
- 优化标签分页查询添加软删除过滤条件
2026-03-04 12:50:10 +08:00
56656693c3 feat(ai-sample): 添加样本批量下载功能
- 在AiSampleController中新增/download接口支持批量下载
- 实现downloadSamples方法处理单个和多个样本下载逻辑
- 集成HttpUtil下载文件并使用ZipOutputStream打包多文件
- 添加文件服务器地址配置注入
2026-02-28 11:48:10 +08:00
2bd20404ba refactor(ai): 重构AI模块文件处理和模型管理功能
- 修改AbstractFileClient移除域名拼接逻辑
- 将getModel接口改为getEnabledModels批量获取已启用模型
- 更新AiSampleMapper查询语句改用LEFT JOIN并添加筛选条件
- 移除application-local.yaml中的file-server配置项
- 添加ImgJsonSerializer组件处理图片URL序列化
- 优化AiSampleServiceImpl文件上传逻辑
- 调整分页参数验证注解格式
2026-02-28 10:19:04 +08:00
4fbe69062c feat(ai): 更新文件服务依赖配置
- 将文件服务API依赖从 infraapi 模块迁移到 fileapi 模块
- 在 pom.xml 中添加 tashow-file-api 依赖
- 更新 application-local.yaml 配置文件中的文件服务配置
- 修改 RpcConfiguration 配置类中的文件API引用路径
- 添加外部服务配置项包括文件服务URL和AI翻译服务URL
2026-02-27 15:33:44 +08:00
5b5b827bb7 feat(ai): 添加AI模型管理和分段上传功能
- 创建AI模型版本管理表tz_ai_model并添加相关索引
- 添加AiModelController、AiModelService等完整的模型管理接口
- 实现模型创建、更新、删除、分页查询等功能
- 添加模型状态更新功能支持启用禁用等状态变更
- 在S3FileClient中实现分段上传uploadMultipart方法
- 扩展FileApi接口增加createFileMultipart分段上传接口
- 修改Nacos配置将命名空间从dev改为具体ID值
- 在SecurityConfiguration中开放AI模型管理接口权限
2026-02-27 15:17:11 +08:00
3438ea40a4 Merge branch 'refs/heads/feature/order' into feature/zijie 2026-02-27 11:02:30 +08:00
29cdf6c581 feat(ai): 添加AI模型管理和分段上传功能
- 创建AI模型版本管理表tz_ai_model并添加相关索引
- 添加AiModelController、AiModelService等完整的模型管理接口
- 实现模型创建、更新、删除、分页查询等功能
- 添加模型状态更新功能支持启用禁用等状态变更
- 在S3FileClient中实现分段上传uploadMultipart方法
- 扩展FileApi接口增加createFileMultipart分段上传接口
- 修改Nacos配置将命名空间从dev改为具体ID值
- 在SecurityConfiguration中开放AI模型管理接口权限
2026-02-27 11:01:44 +08:00
62b78b40be 调整样本 2026-02-27 10:54:48 +08:00
0c051643d5 订单模块调整 2026-01-05 18:24:20 +08:00
793a810d65 调整文件服务 2025-11-19 09:26:55 +08:00
b4f485db09 调整文件服务 2025-11-04 16:57:21 +08:00
fd5a68c27e 添加文件服务 2025-11-04 16:13:28 +08:00
6a59e27ebb 调整框架及订单模块 2025-11-04 09:51:06 +08:00
323cae015f 调整框架及订单模块 2025-11-03 17:36:17 +08:00
4f9b9c29a3 调整订单模块 2025-11-03 10:15:22 +08:00
xuelijun
720937581f 库存 2025-10-31 15:16:15 +08:00
xuelijun
261a8b4797 商品详情1 2025-10-29 16:48:35 +08:00
c283800915 调整产品 2025-10-29 16:37:25 +08:00
483abcfd83 调整用户接口 2025-10-29 15:07:49 +08:00
4ebe4f9ac6 添加订单接口 2025-10-28 09:34:17 +08:00
90e28f056a 添加订单接口 2025-10-27 16:51:14 +08:00
a5a10898a2 Merge branch 'refs/heads/feature/xuelijun' into feature/order 2025-10-27 14:28:59 +08:00
xuelijun
00d4e0c3e8 商品详情 2025-10-27 14:11:03 +08:00
4042fd297d 订单详情接口调整 2025-10-27 09:10:03 +08:00
xuelijun
0ce1eae772 图片 2025-10-25 10:44:32 +08:00
xuelijun
3497962627 Merge branch 'feature/order' into xlj 2025-10-24 17:30:30 +08:00
xuelijun
15e801e426 Merge remote-tracking branch 'origin/feature/order' into feature/order 2025-10-24 17:29:48 +08:00
xuelijun
b2e0cca804 图片 2025-10-24 17:29:14 +08:00
22b96b6feb 订单详情接口调整 2025-10-24 16:50:36 +08:00
fc62fa3167 订单详情接口调整 2025-10-23 13:34:32 +08:00
190e0e1605 订单详情接口调整 2025-10-17 18:32:04 +08:00
3fbb1383fd 订单详情接口调整 2025-10-16 16:49:36 +08:00
xuelijun
7fdf546585 Merge remote-tracking branch 'origin/feature/order' into xlj 2025-10-16 15:30:47 +08:00
368 changed files with 5855 additions and 3515 deletions

View File

@@ -111,3 +111,26 @@ CREATE TABLE `tz_ai_dialog_message`
) ENGINE = InnoDB COMMENT ='ai-对话消息表'; ) ENGINE = InnoDB COMMENT ='ai-对话消息表';
DROP TABLE IF EXISTS `tz_ai_model`;
CREATE TABLE `tz_ai_model`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`model_name` varchar(128) NOT NULL COMMENT '模型名称',
`version` varchar(32) NOT NULL COMMENT '版本号',
`load_percentage` decimal(5,4) NULL DEFAULT 0.0000 COMMENT '负载百分比',
`status` tinyint NOT NULL DEFAULT 0 COMMENT '状态0-禁用 1-启用 2-测试中 3-已废弃)',
`description` varchar(500) NULL DEFAULT '' COMMENT '版本描述',
`creator` varchar(64) NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) NULL DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_model_name` (`model_name` ASC) USING BTREE,
INDEX `idx_version` (`version` ASC) USING BTREE,
INDEX `idx_status` (`status` ASC) USING BTREE,
INDEX `idx_create_time` (`create_time` ASC) USING BTREE
) ENGINE = InnoDB COMMENT = 'AI模型版本管理表';

View File

@@ -1,51 +0,0 @@
-- 会员用户表 (tz_member_user)
CREATE TABLE `tz_member_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`mobile` varchar(11) NOT NULL COMMENT '手机',
`password` varchar(128) NOT NULL COMMENT '加密后的密码',
`status` tinyint(4) NOT NULL COMMENT '帐号状态 (枚举 CommonStatusEnum)',
`register_ip` varchar(32) DEFAULT NULL COMMENT '注册 IP',
`register_terminal` tinyint(4) DEFAULT NULL COMMENT '注册终端 (枚举 TerminalEnum)',
`login_ip` varchar(32) DEFAULT NULL COMMENT '最后登录IP',
`login_date` datetime DEFAULT NULL COMMENT '最后登录时间',
`nickname` varchar(64) DEFAULT NULL COMMENT '用户昵称',
`avatar` varchar(512) DEFAULT NULL COMMENT '用户头像',
`name` varchar(64) DEFAULT NULL COMMENT '真实名字',
`sex` tinyint(4) DEFAULT NULL COMMENT '性别 (枚举 SexEnum)',
`birthday` datetime DEFAULT NULL COMMENT '出生日期',
`area_id` int(11) DEFAULT NULL COMMENT '所在地 (关联 Area.id 字段)',
`mark` varchar(512) DEFAULT NULL COMMENT '用户备注',
`point` int(11) DEFAULT NULL COMMENT '积分',
`tag_ids` varchar(512) DEFAULT NULL COMMENT '会员标签列表,以逗号分隔',
`level_id` bigint(20) DEFAULT NULL COMMENT '会员级别编号 (关联 MemberLevelDO.id 字段)',
`experience` int(11) DEFAULT NULL COMMENT '会员经验',
`group_id` bigint(20) DEFAULT NULL COMMENT '用户分组编号 (关联 MemberGroupDO.id 字段)',
`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '最后更新时间',
`creator` varchar(64) NOT NULL COMMENT '创建者',
`updater` varchar(64) NOT NULL COMMENT '更新者',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_mobile` (`mobile`),
KEY `idx_level_id` (`level_id`),
KEY `idx_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员用户表';
-- 用户收件地址表 (tz_member_address)
CREATE TABLE `tz_member_address` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`user_id` bigint(20) NOT NULL COMMENT '用户编号',
`name` varchar(64) NOT NULL COMMENT '收件人名称',
`mobile` varchar(11) NOT NULL COMMENT '手机号',
`area_id` bigint(20) NOT NULL COMMENT '地区编号',
`detail_address` varchar(512) NOT NULL COMMENT '收件详细地址',
`default_status` tinyint(1) DEFAULT NULL COMMENT '是否默认 (true - 默认收件地址)',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '最后更新时间',
`creator` varchar(64) NOT NULL COMMENT '创建者',
`updater` varchar(64) NOT NULL COMMENT '更新者',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户收件地址表';

View File

@@ -3,8 +3,6 @@ CREATE TABLE `tz_trade_order`
( (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单编号,主键自增', `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单编号,主键自增',
`order_num` varchar(64) NOT NULL COMMENT '订单流水号', `order_num` varchar(64) NOT NULL COMMENT '订单流水号',
`order_category_id` bigint NOT NULL COMMENT '订单类目id',
`order_category_name` varchar(64) NOT NULL COMMENT '订单类目名称',
`order_type` tinyint NOT NULL COMMENT '订单类型 (枚举 TradeOrderTypeEnum)', `order_type` tinyint NOT NULL COMMENT '订单类型 (枚举 TradeOrderTypeEnum)',
`order_terminal` tinyint NOT NULL COMMENT '订单来源 (枚举 TerminalEnum)', `order_terminal` tinyint NOT NULL COMMENT '订单来源 (枚举 TerminalEnum)',
`order_status` tinyint NOT NULL COMMENT '订单状态 (枚举 TradeOrderStatusEnum)', `order_status` tinyint NOT NULL COMMENT '订单状态 (枚举 TradeOrderStatusEnum)',
@@ -17,19 +15,18 @@ CREATE TABLE `tz_trade_order`
`cancel_time` datetime DEFAULT NULL COMMENT '订单取消时间', `cancel_time` datetime DEFAULT NULL COMMENT '订单取消时间',
`cancel_type` tinyint DEFAULT NULL COMMENT '取消类型 (枚举 TradeOrderCancelTypeEnum)', `cancel_type` tinyint DEFAULT NULL COMMENT '取消类型 (枚举 TradeOrderCancelTypeEnum)',
`cancel_reason` varchar(128) DEFAULT NULL COMMENT '取消原因', `cancel_reason` varchar(128) DEFAULT NULL COMMENT '取消原因',
`cancel_remark` varchar(255) DEFAULT NULL COMMENT '取消原因备注',
`merchant_id` bigint DEFAULT NULL COMMENT '商家编号', `merchant_id` bigint DEFAULT NULL COMMENT '商家编号',
`merchant_name` varchar(64) DEFAULT NULL COMMENT '商家名称', `merchant_name` varchar(64) DEFAULT NULL COMMENT '商家名称',
`merchant_remark` varchar(512) DEFAULT NULL COMMENT '商家备注', `merchant_remark` varchar(512) DEFAULT NULL COMMENT '商家备注',
`comment_status` tinyint(1) DEFAULT NULL COMMENT '是否评价 (true-已评价, false-未评价)', `comment_status` tinyint(1) DEFAULT NULL COMMENT '是否评价 (true-已评价, false-未评价)',
`sub_type` tinyint(1) NOT NULL DEFAULT 1 COMMENT '预约类型1预约 2 加急)', `expense_price` int DEFAULT NULL COMMENT '商品成本(单),单位:分',
`sub_time` datetime NOT NULL COMMENT '预约时间', `price` int DEFAULT NULL COMMENT '商品原价(单),单位:分',
`expense_price` int(11) DEFAULT NULL COMMENT '商品成本(单),单位:分', `discount_price` int DEFAULT NULL COMMENT '优惠金额(总),单位:分',
`price` int(11) DEFAULT NULL COMMENT '商品原价(单),单位:分', `delivery_price` int DEFAULT NULL COMMENT '运费金额(总),单位:分',
`discount_price` int(11) DEFAULT NULL COMMENT '优惠金额(总),单位:分', `adjust_price` int DEFAULT NULL COMMENT '订单调价(总),单位:分',
`delivery_price` int(11) DEFAULT NULL COMMENT '运费金额(总),单位:分', `pay_price` int DEFAULT NULL COMMENT '应付金额(总),单位:分',
`adjust_price` int(11) DEFAULT NULL COMMENT '订单调价(总),单位:分', `live_price` int DEFAULT NULL COMMENT '实收金额(总),单位:分',
`pay_price` int(11) DEFAULT NULL COMMENT '应付金额(总),单位:分',
`live_price` int(11) DEFAULT NULL COMMENT '实收金额(总),单位:分',
`pay_order_id` bigint DEFAULT NULL COMMENT '支付订单编号', `pay_order_id` bigint DEFAULT NULL COMMENT '支付订单编号',
`pay_status` tinyint(1) DEFAULT NULL COMMENT '是否已支付 (true-已支付, false-未支付)', `pay_status` tinyint(1) DEFAULT NULL COMMENT '是否已支付 (true-已支付, false-未支付)',
`pay_type` tinyint DEFAULT NULL COMMENT '支付方式PayTypeEnum', `pay_type` tinyint DEFAULT NULL COMMENT '支付方式PayTypeEnum',
@@ -59,6 +56,7 @@ CREATE TABLE `tz_trade_order`
`creator` varchar(64) DEFAULT NULL COMMENT '创建者', `creator` varchar(64) DEFAULT NULL COMMENT '创建者',
`updater` varchar(64) DEFAULT NULL COMMENT '更新者', `updater` varchar(64) DEFAULT NULL COMMENT '更新者',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除', `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT '1' COMMENT '租户编号',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_num` (`order_num`), UNIQUE KEY `uk_order_num` (`order_num`),
KEY `idx_user_id` (`user_id`), KEY `idx_user_id` (`user_id`),
@@ -73,43 +71,56 @@ CREATE TABLE `tz_trade_order`
-- 交易订单项表 (trade_order_item) -- 交易订单项表 (trade_order_item)
CREATE TABLE `tz_trade_order_item` CREATE TABLE `tz_trade_order_item`
( (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号', `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`user_id` bigint(20) NOT NULL COMMENT '用户编号', `user_id` bigint NOT NULL COMMENT '用户编号',
`order_id` bigint(20) NOT NULL COMMENT '订单编号', `order_id` bigint NOT NULL COMMENT '订单编号',
`cart_id` bigint(20) DEFAULT NULL COMMENT '购物车项编号', `cart_id` bigint DEFAULT NULL COMMENT '购物车项编号',
`spu_id` bigint(20) NOT NULL COMMENT '商品 SPU 编号', `spu_id` bigint NOT NULL COMMENT '商品 SPU 编号',
`spu_name` varchar(256) NOT NULL COMMENT '商品 SPU 名称', `spu_name` varchar(256) NOT NULL COMMENT '商品 SPU 名称',
`spu_type` tinyint(1) NOT NULL COMMENT '商品类型1商品 2服务', `spu_type` tinyint(1) NOT NULL COMMENT '商品类型1商品 2服务',
`sku_id` bigint(20) NOT NULL COMMENT '商品 SKU 编号', `sku_id` bigint NOT NULL COMMENT '商品 SKU 编号',
`sku_name` bigint(20) NOT NULL COMMENT '商品 SKU 名称', `sku_name` varchar(255) NOT NULL COMMENT '商品 SKU 名称',
`shop_id` bigint DEFAULT NULL COMMENT '店铺id',
`shop_name` varchar(64) DEFAULT NULL COMMENT '店铺名称',
`shop_logo` varchar(255) DEFAULT NULL COMMENT '店铺logo',
`order_category_name` varchar(64) NOT NULL COMMENT '订单类目名称',
`order_category_id` bigint NOT NULL COMMENT '订单类目id',
`pic_url` varchar(512) NOT NULL COMMENT '商品图片', `pic_url` varchar(512) NOT NULL COMMENT '商品图片',
`count` int(11) NOT NULL COMMENT '购买数量', `count` int NOT NULL COMMENT '购买数量',
`unit` varchar(16) NOT NULL COMMENT '商品单位', `unit` varchar(16) DEFAULT NULL COMMENT '商品单位',
`expense_price` int(11) NOT NULL COMMENT '商品成本(单),单位:分', `expense_price` int DEFAULT NULL COMMENT '商品成本(单),单位:分',
`price` int(11) NOT NULL COMMENT '商品原价(单),单位:分', `price` int DEFAULT NULL COMMENT '商品原价(单),单位:分',
`discount_price` int(11) NOT NULL COMMENT '优惠金额(总),单位:分', `discount_price` int DEFAULT NULL COMMENT '优惠金额(总),单位:分',
`delivery_price` int(11) NOT NULL COMMENT '运费金额(总),单位:分', `delivery_price` int DEFAULT NULL COMMENT '运费金额(总),单位:分',
`adjust_price` int(11) NOT NULL COMMENT '订单调价(总),单位:分', `adjust_price` int DEFAULT NULL COMMENT '订单调价(总),单位:分',
`pay_price` int(11) NOT NULL COMMENT '应付金额(总),单位:分', `pay_price` int DEFAULT NULL COMMENT '应付金额(总),单位:分',
`live_price` int(11) NOT NULL COMMENT '实收金额(总),单位:分', `live_price` int DEFAULT NULL COMMENT '实收金额(总),单位:分',
`refund_status` tinyint DEFAULT NULL COMMENT '退款状态 (枚举 TradeOrderRefundStatusEnum)',
`serve_address` datetime NOT NULL COMMENT '服务地址', `refund_price` int DEFAULT NULL COMMENT '退款金额,单位:分',
`refund_time` datetime DEFAULT NULL COMMENT '退款时间',
`sub_type` tinyint(1) DEFAULT '1' COMMENT '预约类型1预约 2 加急)',
`sub_time` datetime DEFAULT NULL COMMENT '预约时间',
`serve_address` varchar(255) DEFAULT NULL COMMENT '服务地址',
`serve_content` varchar(255) DEFAULT NULL COMMENT '服务内容',
`properties` json DEFAULT NULL COMMENT '属性数组', `properties` json DEFAULT NULL COMMENT '属性数组',
`serve_info` json DEFAULT NULL COMMENT '服务信息', `serve_info` json DEFAULT NULL COMMENT '服务信息',
`serve_ext_info` json DEFAULT NULL COMMENT '扩展服务信息,存储额外的服务相关数据', `serve_ext_info` json DEFAULT NULL COMMENT '扩展服务信息,存储额外的服务相关数据',
`price_ext_info` json DEFAULT NULL COMMENT '附加费信息', `price_ext_info` json DEFAULT NULL COMMENT '附加费信息',
`version` int(11) DEFAULT '0' COMMENT '版本号(乐观锁)', `version` int NOT NULL DEFAULT '0' COMMENT '版本号(乐观锁)',
`create_time` datetime NOT NULL COMMENT '创建时间', `create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '最后更新时间', `update_time` datetime DEFAULT NULL COMMENT '最后更新时间',
`creator` varchar(64) NOT NULL COMMENT '创建者', `creator` varchar(64) DEFAULT NULL COMMENT '创建者',
`updater` varchar(64) NOT NULL COMMENT '更新者', `updater` varchar(64) DEFAULT NULL COMMENT '更新者',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除', `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT '1' COMMENT '租户编号',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `idx_order_id` (`order_id`), KEY `idx_order_id` (`order_id`),
KEY `idx_sku_id` (`sku_id`), KEY `idx_sku_id` (`sku_id`),
KEY `idx_user_id` (`user_id`) KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB ) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='交易订单项表'; AUTO_INCREMENT = 2
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci COMMENT ='交易订单项表';
-- 订单日志表 (trade_order_log) -- 订单日志表 (trade_order_log)
@@ -128,6 +139,7 @@ CREATE TABLE `tz_trade_order_log`
`creator` varchar(64) NOT NULL COMMENT '创建者', `creator` varchar(64) NOT NULL COMMENT '创建者',
`updater` varchar(64) NOT NULL COMMENT '更新者', `updater` varchar(64) NOT NULL COMMENT '更新者',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除', `deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT '1' COMMENT '租户编号',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `idx_order_id` (`order_id`), KEY `idx_order_id` (`order_id`),
KEY `idx_user_id` (`user_id`) KEY `idx_user_id` (`user_id`)
@@ -307,4 +319,20 @@ CREATE TABLE `tz_trade_delivery_pick_up_store`
) ENGINE = InnoDB ) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='自提门店表'; DEFAULT CHARSET = utf8mb4 COMMENT ='自提门店表';
-- 订单预约时间修改记录表 (tz_trade_sub_log)
CREATE TABLE `tz_trade_sub_log`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`user_id` bigint(20) NOT NULL COMMENT '用户编号',
`order_id` bigint(20) NOT NULL COMMENT '订单号',
`sub_time` datetime NOT NULL COMMENT '预约时间',
`create_time` datetime NOT NULL COMMENT '创建时间',
`creator` varchar(64) NOT NULL COMMENT '创建者',
`updater` varchar(64) NOT NULL COMMENT '更新者',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='订单预约时间修改记录表';

90
sql/mysql/user.sql Normal file
View File

@@ -0,0 +1,90 @@
-- 登录用户表 (tz_login_user) 后续拓展用户信息表
CREATE TABLE `tz_user_login`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`mobile` varchar(11) NOT NULL COMMENT '手机',
`password` varchar(128) NOT NULL COMMENT '加密后的密码',
`status` tinyint(4) NOT NULL COMMENT '帐号状态 (枚举 CommonStatusEnum)',
`register_ip` varchar(32) DEFAULT NULL COMMENT '注册 IP',
`register_terminal` tinyint(4) DEFAULT NULL COMMENT '注册渠道 (枚举 TerminalEnum)',
`register_date` datetime DEFAULT NULL COMMENT '注册时间',
`login_ip` varchar(32) DEFAULT NULL COMMENT '最后登录IP',
`login_date` datetime DEFAULT NULL COMMENT '最后登录时间',
`login_terminal` varchar(32) DEFAULT NULL COMMENT '最后登录设备',
`nickname` varchar(64) DEFAULT NULL COMMENT '用户昵称',
`avatar` varchar(512) DEFAULT NULL COMMENT '用户头像',
`remark` varchar(512) DEFAULT NULL COMMENT '用户备注',
`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '最后更新时间',
`creator` varchar(64) NOT NULL COMMENT '创建者',
`updater` varchar(64) NOT NULL COMMENT '更新者',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_mobile` (`mobile`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='登录用户表';
-- 会员信息表 (tz_user_member)
CREATE TABLE `tz_user_member`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`name` varchar(64) DEFAULT NULL COMMENT '真实名字',
`sex` tinyint DEFAULT NULL COMMENT '性别 (枚举 SexEnum)',
`birthday` datetime DEFAULT NULL COMMENT '出生日期',
`area_id` int DEFAULT NULL COMMENT '所在地 (关联 Area.id 字段)',
`point` int DEFAULT NULL COMMENT '积分',
`tag_ids` varchar(512) DEFAULT NULL COMMENT '会员标签列表,以逗号分隔',
`level_id` bigint DEFAULT NULL COMMENT '会员级别编号 (关联 MemberLevelDO.id 字段)',
`experience` int DEFAULT NULL COMMENT '会员经验',
`group_id` bigint DEFAULT NULL COMMENT '用户分组编号 (关联 MemberGroupDO.id 字段)',
`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '最后更新时间',
`creator` varchar(64) NOT NULL COMMENT '创建者',
`updater` varchar(64) NOT NULL COMMENT '更新者',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='会员信息表';
-- 会员地址表 (tz_user_member_address)
CREATE TABLE `tz_user_member_address`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户编号',
`name` varchar(128) DEFAULT NULL COMMENT '收件人名称',
`mobile` varchar(11) DEFAULT NULL COMMENT '手机号',
`areaId` bigint(20) DEFAULT NULL COMMENT '地区编号',
`detailAddress` varchar(255) DEFAULT NULL COMMENT '收件详细地址',
`defaultStatus` int DEFAULT NULL COMMENT '是否默认',
`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '最后更新时间',
`creator` varchar(64) NOT NULL COMMENT '创建者',
`updater` varchar(64) NOT NULL COMMENT '更新者',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='会员地址表';
-- 会员等级表 (tz_user_member_level)
CREATE TABLE `tz_user_member_level`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`level_name` varchar(64) DEFAULT NULL COMMENT '等级名称',
`level` int DEFAULT NULL COMMENT '性别 (枚举 SexEnum)',
`experience` int DEFAULT NULL COMMENT '会员经验',
`discount_percent` int DEFAULT NULL COMMENT '享受折扣',
`icon` varchar(128) DEFAULT NULL COMMENT '等级图标',
`background_url` varchar(128) DEFAULT NULL COMMENT '等级背景图',
`status` int DEFAULT NULL COMMENT '状态',
`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '最后更新时间',
`creator` varchar(64) NOT NULL COMMENT '创建者',
`updater` varchar(64) NOT NULL COMMENT '更新者',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='会员等级表';

View File

@@ -163,6 +163,16 @@
<artifactId>tashow-trade-api</artifactId> <artifactId>tashow-trade-api</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-module-file</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-file-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency> <dependency>
<groupId>com.tashow.cloud</groupId> <groupId>com.tashow.cloud</groupId>
<artifactId>tashow-module-trade</artifactId> <artifactId>tashow-module-trade</artifactId>
@@ -180,7 +190,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tashow.cloud</groupId> <groupId>com.tashow.cloud</groupId>
<artifactId>tashow-member-api</artifactId> <artifactId>tashow-user-api</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -193,6 +203,11 @@
<artifactId>tashow-sdk-payment</artifactId> <artifactId>tashow-sdk-payment</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-product-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- Spring 核心 --> <!-- Spring 核心 -->
<dependency> <dependency>

View File

@@ -16,7 +16,8 @@
<module>tashow-product-api</module> <module>tashow-product-api</module>
<module>tashow-trade-api</module> <module>tashow-trade-api</module>
<module>tashow-pay-api</module> <module>tashow-pay-api</module>
<module>tashow-member-api</module> <module>tashow-user-api</module>
<module>tashow-file-api</module>
</modules> </modules>
</project> </project>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-feign</artifactId>
<version>${revision}</version>
</parent>
<artifactId>tashow-file-api</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
infra 模块 API暴露给其它模块调用
</description>
<dependencies>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-common</artifactId>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,18 @@
package com.tashow.cloud.fileapi.api.config;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.fileapi.enums.ApiConstants;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
/** RPC 服务 - 参数配置 */
public interface ConfigApi {
String PREFIX = ApiConstants.PREFIX + "/config";
/** 根据参数键查询参数值 */
@GetMapping(PREFIX + "/get-value-by-key")
CommonResult<String> getConfigValueByKey(@RequestParam("key") String key);
}

View File

@@ -0,0 +1,68 @@
package com.tashow.cloud.fileapi.api.file;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.fileapi.api.file.dto.FileCreateReqDTO;
import com.tashow.cloud.fileapi.enums.ApiConstants;
import jakarta.validation.Valid;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
/**
* RPC 服务 - 文件
*/
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
public interface FileApi {
String PREFIX = ApiConstants.PREFIX + "/file";
/**
* 保存文件,并返回文件的访问路径
*
* @param content 文件内容
* @return 文件路径
*/
default String createFile(byte[] content) {
return createFile(null, null, content);
}
/**
* 保存文件,并返回文件的访问路径
*
* @param path 文件路径
* @param content 文件内容
* @return 文件路径
*/
default String createFile(String path, byte[] content) {
return createFile(null, path, content);
}
/**
* 保存文件,并返回文件的访问路径
*
* @param name 原文件名称
* @param path 文件路径
* @param content 文件内容
* @return 文件路径
*/
default String createFile(
@RequestParam("name") String name,
@RequestParam("path") String path,
@RequestParam("content") byte[] content) {
return createFile(new FileCreateReqDTO().setName(name).setPath(path).setContent(content))
.getCheckedData();
}
/**
* 保存文件,并返回文件的访问路径
*/
@PostMapping(PREFIX + "/create")
CommonResult<String> createFile(@Valid @RequestBody FileCreateReqDTO createReqDTO);
/**
* 分段上传文件
*/
@PostMapping(PREFIX + "/create-multipart")
CommonResult<String> createFileMultipart(@Valid @RequestBody FileCreateReqDTO createReqDTO);
}

View File

@@ -0,0 +1,24 @@
package com.tashow.cloud.fileapi.api.file.dto;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import lombok.experimental.Accessors;
/** RPC 服务 - 文件创建 Request DTO */
@Data
@Accessors(chain = true)
public class FileCreateReqDTO {
/** 原文件名称 */
private String name;
/** 文件路径 */
private String path;
/** 文件访问地址 */
private String url;
/** 文件内容 */
@NotEmpty(message = "文件内容不能为空")
private byte[] content;
}

View File

@@ -0,0 +1,4 @@
/**
* infra API 包,定义暴露给其它模块的 API
*/
package com.tashow.cloud.fileapi.api;

View File

@@ -1,4 +1,4 @@
package com.tashow.cloud.memberapi.enums; package com.tashow.cloud.fileapi.enums;
import com.tashow.cloud.common.enums.RpcConstants; import com.tashow.cloud.common.enums.RpcConstants;
@@ -15,9 +15,9 @@ public class ApiConstants {
* *
* 注意需要保证和 spring.application.name 保持一致 * 注意需要保证和 spring.application.name 保持一致
*/ */
public static final String NAME = "member-server"; public static final String NAME = "file-server";
public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/member"; public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/file";
public static final String VERSION = "1.0.0"; public static final String VERSION = "1.0.0";

View File

@@ -0,0 +1,72 @@
package com.tashow.cloud.fileapi.enums;
import com.tashow.cloud.common.exception.ErrorCode;
/**
* Infra 错误码枚举类
*
* infra 系统,使用 1-001-000-000 段
*/
public interface ErrorCodeConstants {
// ========== 参数配置 1-001-000-000 ==========
ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(1_001_000_001, "参数配置不存在");
ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1_001_000_002, "参数配置 key 重复");
ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1_001_000_003, "不能删除类型为系统内置的参数配置");
ErrorCode CONFIG_GET_VALUE_ERROR_IF_VISIBLE = new ErrorCode(1_001_000_004, "获取参数配置失败,原因:不允许获取不可见配置");
// ========== 定时任务 1-001-001-000 ==========
ErrorCode JOB_NOT_EXISTS = new ErrorCode(1_001_001_000, "定时任务不存在");
ErrorCode JOB_HANDLER_EXISTS = new ErrorCode(1_001_001_001, "定时任务的处理器已经存在");
ErrorCode JOB_CHANGE_STATUS_INVALID = new ErrorCode(1_001_001_002, "只允许修改为开启或者关闭状态");
ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, "定时任务已经处于该状态,无需修改");
ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, "只有开启状态的任务,才可以修改");
ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1_001_001_005, "CRON 表达式不正确");
ErrorCode JOB_HANDLER_BEAN_NOT_EXISTS = new ErrorCode(1_001_001_006, "定时任务的处理器 Bean 不存在,注意 Bean 默认首字母小写");
ErrorCode JOB_HANDLER_BEAN_TYPE_ERROR = new ErrorCode(1_001_001_007, "定时任务的处理器 Bean 类型不正确,未实现 JobHandler 接口");
// ========== API 错误日志 1-001-002-000 ==========
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1_001_002_000, "API 错误日志不存在");
ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1_001_002_001, "API 错误日志已处理");
// ========= 文件相关 1-001-003-000 =================
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1_001_003_000, "文件路径已存在");
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1_001_003_001, "文件不存在");
ErrorCode FILE_IS_EMPTY = new ErrorCode(1_001_003_002, "文件为空");
// ========== 代码生成器 1-001-004-000 ==========
ErrorCode CODEGEN_TABLE_EXISTS = new ErrorCode(1_001_004_002, "表定义已经存在");
ErrorCode CODEGEN_IMPORT_TABLE_NULL = new ErrorCode(1_001_004_001, "导入的表不存在");
ErrorCode CODEGEN_IMPORT_COLUMNS_NULL = new ErrorCode(1_001_004_002, "导入的字段不存在");
ErrorCode CODEGEN_TABLE_NOT_EXISTS = new ErrorCode(1_001_004_004, "表定义不存在");
ErrorCode CODEGEN_COLUMN_NOT_EXISTS = new ErrorCode(1_001_004_005, "字段义不存在");
ErrorCode CODEGEN_SYNC_COLUMNS_NULL = new ErrorCode(1_001_004_006, "同步的字段不存在");
ErrorCode CODEGEN_SYNC_NONE_CHANGE = new ErrorCode(1_001_004_007, "同步失败,不存在改变");
ErrorCode CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL = new ErrorCode(1_001_004_008, "数据库的表注释未填写");
ErrorCode CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL = new ErrorCode(1_001_004_009, "数据库的表字段({})注释未填写");
ErrorCode CODEGEN_MASTER_TABLE_NOT_EXISTS = new ErrorCode(1_001_004_010, "主表(id={})定义不存在,请检查");
ErrorCode CODEGEN_SUB_COLUMN_NOT_EXISTS = new ErrorCode(1_001_004_011, "子表的字段(id={})不存在,请检查");
ErrorCode CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE = new ErrorCode(1_001_004_012, "主表生成代码失败,原因:它没有子表");
// ========== 文件配置 1-001-006-000 ==========
ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_006_000, "文件配置不存在");
ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1_001_006_001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件");
// ========== 数据源配置 1-001-007-000 ==========
ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_007_000, "数据源配置不存在");
ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1_001_007_001, "数据源配置不正确,无法进行连接");
// ========== 学生 1-001-201-000 ==========
ErrorCode DEMO01_CONTACT_NOT_EXISTS = new ErrorCode(1_001_201_000, "示例联系人不存在");
ErrorCode DEMO02_CATEGORY_NOT_EXISTS = new ErrorCode(1_001_201_001, "示例分类不存在");
ErrorCode DEMO02_CATEGORY_EXITS_CHILDREN = new ErrorCode(1_001_201_002, "存在存在子示例分类,无法删除");
ErrorCode DEMO02_CATEGORY_PARENT_NOT_EXITS = new ErrorCode(1_001_201_003,"父级示例分类不存在");
ErrorCode DEMO02_CATEGORY_PARENT_ERROR = new ErrorCode(1_001_201_004, "不能设置自己为父示例分类");
ErrorCode DEMO02_CATEGORY_NAME_DUPLICATE = new ErrorCode(1_001_201_005, "已经存在该名字的示例分类");
ErrorCode DEMO02_CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_001_201_006, "不能设置自己的子示例分类为父示例分类");
ErrorCode DEMO03_STUDENT_NOT_EXISTS = new ErrorCode(1_001_201_007, "学生不存在");
ErrorCode DEMO03_GRADE_NOT_EXISTS = new ErrorCode(1_001_201_008, "学生班级不存在");
ErrorCode DEMO03_GRADE_EXISTS = new ErrorCode(1_001_201_009, "学生班级已存在");
}

View File

@@ -1,4 +0,0 @@
/**
* 消息队列的消息
*/
package com.tashow.cloud.memberapi.message;

View File

@@ -0,0 +1,69 @@
package com.tashow.cloud.productapi.api.product;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.productapi.api.product.dto.ProdDO;
import com.tashow.cloud.productapi.api.product.vo.prod.ProdListVO;
import com.tashow.cloud.productapi.api.product.vo.prod.ProdPageReqVO;
import com.tashow.cloud.productapi.api.product.vo.prod.ProdServiceVO;
import com.tashow.cloud.productapi.enums.ApiConstants;
import jakarta.annotation.security.PermitAll;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
/** RPC 服务 - 参数配置 */
public interface ProdApi {
String PREFIX = ApiConstants.PREFIX + "/prod";
/**
* 获取商品详情
*
* @param id 商品id
* @return 编号
*/
@GetMapping(PREFIX + "/getProdInfo")
@PermitAll
CommonResult<ProdDO> getProdInfo(@RequestParam(value = "id", required = false) Long id);
/**
* 获取商品服务配置
*
* @param id 商品id
* @return 编号
*/
@GetMapping(PREFIX + "/getProdService")
ProdServiceVO getProdService(@RequestParam(value = "id", required = false) Long id);
/**
* 获得商品分页
*
* @param pageReqVO 分页查询
* @return 商品分页
*/
@GetMapping(PREFIX + "/getProdPage")
PageResult<ProdListVO> getProdPage(ProdPageReqVO pageReqVO);
/**
* 扣减库存
* @param skuId 单品ID
* @param stocksNum 扣减数量
*/
@PostMapping(PREFIX + "/reduceStocks")
CommonResult<Boolean> reduceStocks(@RequestParam(value = "skuId", required = false)Long skuId
,@RequestParam(value = "stocksNum", required = false) Integer stocksNum
) ;
/**
* 增加库存
* @param skuId 单品ID
* @param stocksNum 增加数量
*/
@PostMapping(PREFIX + "/increaseStocks")
CommonResult<Boolean> increaseStocks(@RequestParam(value = "skuId", required = false)Long skuId
,@RequestParam(value = "stocksNum", required = false) Integer stocksNum);
}

View File

@@ -70,6 +70,10 @@ public class ProdListVO {
* 还剩多少天 * 还剩多少天
*/ */
private Long remainingDays; private Long remainingDays;
/**
* 图片
*/
private String pic;
/** /**
* 审核备注 * 审核备注

View File

@@ -32,4 +32,10 @@ public interface ErrorCodeConstants {
ErrorCode SKU_SERVICES_FORM_NOT_EXISTS = new ErrorCode(10021, "商品SKU扩展服务表单不存在"); ErrorCode SKU_SERVICES_FORM_NOT_EXISTS = new ErrorCode(10021, "商品SKU扩展服务表单不存在");
ErrorCode SKU_SERVICE_TRANSPORT_NOT_EXISTS = new ErrorCode(10022, "服务遗体运输不存在"); ErrorCode SKU_SERVICE_TRANSPORT_NOT_EXISTS = new ErrorCode(10022, "服务遗体运输不存在");
ErrorCode SKU_SERVICE_DETAILS_NOT_EXISTS = new ErrorCode(10023, "服务详情不存在"); ErrorCode SKU_SERVICE_DETAILS_NOT_EXISTS = new ErrorCode(10023, "服务详情不存在");
ErrorCode SKU_DOES_NOT_EXIST = new ErrorCode(10030, "商品不存在");
ErrorCode SKU_DELETE = new ErrorCode(10030, "商品已删除");
ErrorCode SKU_DISABLE = new ErrorCode(10031, "商品已禁用");
} }

View File

@@ -18,7 +18,7 @@ public class TradeOrderRespDTO {
private Long id; private Long id;
//订单流水号 //订单流水号
private String no; private String orderNum;
//订单类型 //订单类型
private Integer type; // 参见 TradeOrderTypeEnum 枚举 private Integer type; // 参见 TradeOrderTypeEnum 枚举

View File

@@ -16,6 +16,7 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_ITEM_NOT_FOUND = new ErrorCode(1_011_000_010, "交易订单项不存在"); ErrorCode ORDER_ITEM_NOT_FOUND = new ErrorCode(1_011_000_010, "交易订单项不存在");
ErrorCode ORDER_NOT_FOUND = new ErrorCode(1_011_000_011, "交易订单不存在"); ErrorCode ORDER_NOT_FOUND = new ErrorCode(1_011_000_011, "交易订单不存在");
ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1_011_000_012, "交易订单项更新售后状态失败,请重试"); ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1_011_000_012, "交易订单项更新售后状态失败,请重试");
ErrorCode ORDER_UPDATE_UNWAITACCEPT_FAIL = new ErrorCode(1_011_000_012, "交易订单更新状态失败,当前订单不处于待验收状态");
ErrorCode ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1_011_000_013, "交易订单更新支付状态失败,订单不是【未支付】状态"); ErrorCode ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1_011_000_013, "交易订单更新支付状态失败,订单不是【未支付】状态");
ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1_011_000_014, "交易订单更新支付状态失败,支付单编号不匹配"); ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1_011_000_014, "交易订单更新支付状态失败,支付单编号不匹配");
ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_015, "交易订单更新支付状态失败,支付单状态不是【支付成功】状态"); ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_015, "交易订单更新支付状态失败,支付单状态不是【支付成功】状态");

View File

@@ -20,40 +20,15 @@ import static cn.hutool.core.util.ArrayUtil.firstMatch;
@Getter @Getter
public enum AfterSaleStatusEnum implements ArrayValuable<Integer> { public enum AfterSaleStatusEnum implements ArrayValuable<Integer> {
/** APPLY(10,"等待审核", ""), // 有赞的状态提示:退款申请待商家处理
* 【申请售后】 SELLER_AGREE(20, "审核通过", ""), // 有赞的状态提示:请退货并填写物流信息
*/ BUYER_DELIVERY(30,"已完成", ""), // 有赞的状态提示:退货退款申请待商家处理
APPLY(10,"申请中", "会员申请退款"), // 有赞的状态提示:退款申请待商家处理 WAIT_REFUND(40, "已取消", ""), // 有赞的状态提示:无(有赞无该状态)
/** COMPLETE(50, "已拒绝", ""), // 有赞的状态提示:退款成功
* 卖家通过售后;【商品待退货】
*/
SELLER_AGREE(20, "卖家通过", "商家同意退款"), // 有赞的状态提示:请退货并填写物流信息
/**
* 买家已退货,等待卖家收货;【商家待收货】
*/
BUYER_DELIVERY(30,"待卖家收货", "会员填写退货物流信息"), // 有赞的状态提示:退货退款申请待商家处理
/**
* 卖家已收货,等待平台退款;等待退款【等待退款】
*/
WAIT_REFUND(40, "等待平台退款", "商家收货"), // 有赞的状态提示:无(有赞无该状态)
/**
* 完成退款【退款成功】
*/
COMPLETE(50, "完成", "商家确认退款"), // 有赞的状态提示:退款成功
/**
* 【买家取消】
*/
BUYER_CANCEL(61, "买家取消售后", "会员取消退款"), // 有赞的状态提示:退款关闭
/**
* 卖家拒绝售后;商家拒绝【商家拒绝】
*/
SELLER_DISAGREE(62,"卖家拒绝", "商家拒绝退款"), // 有赞的状态提示:商家不同意退款申请
/**
* 卖家拒绝收货,终止售后;【商家拒收货】
*/
SELLER_REFUSE(63,"卖家拒绝收货", "商家拒绝收货"), // 有赞的状态提示:商家拒绝收货,不同意退款
; ;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AfterSaleStatusEnum::getStatus).toArray(Integer[]::new); public static final Integer[] ARRAYS = Arrays.stream(values()).map(AfterSaleStatusEnum::getStatus).toArray(Integer[]::new);
/** /**

View File

@@ -25,6 +25,7 @@ public enum TradeOrderOperateTypeEnum {
SYSTEM_COMMENT(34, "到期未评价,系统自动评价"), SYSTEM_COMMENT(34, "到期未评价,系统自动评价"),
MEMBER_CANCEL(40, "取消订单"), MEMBER_CANCEL(40, "取消订单"),
SYSTEM_CANCEL(41, "到期未支付,系统自动取消订单"), SYSTEM_CANCEL(41, "到期未支付,系统自动取消订单"),
ADMIN_CANCEL(42, "管理员取消订单"),
// 42 预留:管理员取消订单 // 42 预留:管理员取消订单
ADMIN_CANCEL_AFTER_SALE(43, "订单全部售后,管理员自动取消订单"), ADMIN_CANCEL_AFTER_SALE(43, "订单全部售后,管理员自动取消订单"),
MEMBER_DELETE(49, "删除订单"), MEMBER_DELETE(49, "删除订单"),

View File

@@ -17,17 +17,15 @@ import java.util.Arrays;
@Getter @Getter
public enum TradeOrderStatusEnum implements ArrayValuable<Integer> { public enum TradeOrderStatusEnum implements ArrayValuable<Integer> {
UNPAID(0, "待支付"), WAITPAID(10, "等待付款"),
UNDELIVERED(10, "待发货"), WAITCONFIRM(20, "等待确定"),
DELIVERED(20, "已发货"), WAITSERVE(30, "等待服务"),
COMPLETED(30, "已完成"), WAITACCEPT(40, "等待验收"),
CANCELED(40, "取消"), COMPLETED(50, "完成"),
CANCELED(60, "已取消"),
WAITPAID(100, "等待付"), DELIVERED(70, "已退"),
WAITCONFIRM(110, "等待确定"), WAITDELIVERED(80, "待发货"),
WAITSERVE(120, "等待服务"), ;
WAITACCEPT(130, "等待验收"),
SERVECANCELED(140, "取消服务");
public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderStatusEnum::getStatus).toArray(Integer[]::new); public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderStatusEnum::getStatus).toArray(Integer[]::new);
@@ -55,7 +53,16 @@ public enum TradeOrderStatusEnum implements ArrayValuable<Integer> {
* @return 是否 * @return 是否
*/ */
public static boolean isUnpaid(Integer status) { public static boolean isUnpaid(Integer status) {
return ObjectUtil.equal(UNPAID.getStatus(), status); return ObjectUtil.equal(WAITPAID.getStatus(), status);
}
/**
* 判断指定状态,是否正处于【待验收】状态
*
* @param status 指定状态
* @return 是否
*/
public static boolean isWaitAccept(Integer status) {
return ObjectUtil.equal(WAITACCEPT.getStatus(), status);
} }
/** /**
@@ -65,7 +72,7 @@ public enum TradeOrderStatusEnum implements ArrayValuable<Integer> {
* @return 是否 * @return 是否
*/ */
public static boolean isUndelivered(Integer status) { public static boolean isUndelivered(Integer status) {
return ObjectUtil.equal(UNDELIVERED.getStatus(), status); return ObjectUtil.equal(WAITDELIVERED.getStatus(), status);
} }
/** /**
@@ -105,7 +112,7 @@ public enum TradeOrderStatusEnum implements ArrayValuable<Integer> {
* @return 是否 * @return 是否
*/ */
public static boolean havePaid(Integer status) { public static boolean havePaid(Integer status) {
return ObjectUtils.equalsAny(status, UNDELIVERED.getStatus(), return ObjectUtils.equalsAny(status, WAITCONFIRM.getStatus(),
DELIVERED.getStatus(), COMPLETED.getStatus()); DELIVERED.getStatus(), COMPLETED.getStatus());
} }

View File

@@ -8,7 +8,7 @@
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>tashow-member-api</artifactId> <artifactId>tashow-user-api</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>

View File

@@ -1,8 +1,8 @@
package com.tashow.cloud.memberapi.api.address; package com.tashow.cloud.userapi.api.address;
import com.tashow.cloud.common.pojo.CommonResult; import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.memberapi.api.address.dto.MemberAddressRespDTO; import com.tashow.cloud.userapi.api.address.dto.MemberAddressRespDTO;
import com.tashow.cloud.memberapi.enums.ApiConstants; import com.tashow.cloud.userapi.enums.ApiConstants;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;

View File

@@ -1,4 +1,4 @@
package com.tashow.cloud.memberapi.api.address.dto; package com.tashow.cloud.userapi.api.address.dto;
import lombok.Data; import lombok.Data;

View File

@@ -1,4 +1,4 @@
/** /**
* member API 定义暴露给其它模块的 API * member API 定义暴露给其它模块的 API
*/ */
package com.tashow.cloud.memberapi.api; package com.tashow.cloud.userapi.api;

View File

@@ -1,8 +1,8 @@
package com.tashow.cloud.memberapi.api.user; package com.tashow.cloud.userapi.api.user;
import com.tashow.cloud.common.pojo.CommonResult; import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.memberapi.api.user.dto.MemberUserRespDTO; import com.tashow.cloud.userapi.api.user.dto.UserMemberRespDTO;
import com.tashow.cloud.memberapi.enums.ApiConstants; import com.tashow.cloud.userapi.enums.ApiConstants;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@@ -17,9 +17,9 @@ import static com.tashow.cloud.common.util.collection.CollectionUtils.convertMap
* RPC 服务 - 会员用户 * RPC 服务 - 会员用户
*/ */
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory = @FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
public interface MemberUserApi { public interface UserMemberApi {
String PREFIX = ApiConstants.PREFIX + "/user"; String PREFIX = ApiConstants.PREFIX + "/member";
/** /**
@@ -28,9 +28,9 @@ public interface MemberUserApi {
* @param ids 用户编号的数组 * @param ids 用户编号的数组
* @return 会员用户 Map * @return 会员用户 Map
*/ */
default Map<Long, MemberUserRespDTO> getUserMap(Collection<Long> ids) { default Map<Long, UserMemberRespDTO> getUserMap(Collection<Long> ids) {
List<MemberUserRespDTO> list = getUserList(ids).getCheckedData(); List<UserMemberRespDTO> list = getUserList(ids).getCheckedData();
return convertMap(list, MemberUserRespDTO::getId); return convertMap(list, UserMemberRespDTO::getId);
} }
/** /**
@@ -39,7 +39,7 @@ public interface MemberUserApi {
* @return * @return
*/ */
@GetMapping(PREFIX + "/get") @GetMapping(PREFIX + "/get")
CommonResult<MemberUserRespDTO> getUser(@RequestParam("id") Long id); CommonResult<UserMemberRespDTO> getUser(@RequestParam("id") Long id);
/** /**
* 获得会员用户信息们 * 获得会员用户信息们
@@ -47,7 +47,7 @@ public interface MemberUserApi {
* @return * @return
*/ */
@GetMapping(PREFIX + "/list") @GetMapping(PREFIX + "/list")
CommonResult<List<MemberUserRespDTO>> getUserList(@RequestParam("ids") Collection<Long> ids); CommonResult<List<UserMemberRespDTO>> getUserList(@RequestParam("ids") Collection<Long> ids);
/** /**
* 基于用户昵称模糊匹配用户列表 * 基于用户昵称模糊匹配用户列表
@@ -55,7 +55,7 @@ public interface MemberUserApi {
* @return * @return
*/ */
@GetMapping(PREFIX + "/list-by-nickname") @GetMapping(PREFIX + "/list-by-nickname")
CommonResult<List<MemberUserRespDTO>> getUserListByNickname(@RequestParam("nickname") String nickname); CommonResult<List<UserMemberRespDTO>> getUserListByNickname(@RequestParam("nickname") String nickname);
/** /**
* 基于手机号精准匹配用户 * 基于手机号精准匹配用户
@@ -63,7 +63,7 @@ public interface MemberUserApi {
* @return * @return
*/ */
@GetMapping(PREFIX + "/get-by-mobile") @GetMapping(PREFIX + "/get-by-mobile")
CommonResult<MemberUserRespDTO> getUserByMobile(@RequestParam("mobile") String mobile); CommonResult<UserMemberRespDTO> getUserByMobile(@RequestParam("mobile") String mobile);
/** /**
* 校验用户是否存在 * 校验用户是否存在

View File

@@ -1,4 +1,4 @@
package com.tashow.cloud.memberapi.api.user.dto; package com.tashow.cloud.userapi.api.user.dto;
import lombok.Data; import lombok.Data;
@@ -8,13 +8,15 @@ import java.time.LocalDateTime;
* RPC 服务 - 用户信息 Response DTO * RPC 服务 - 用户信息 Response DTO
*/ */
@Data @Data
public class MemberUserRespDTO { public class UserMemberRespDTO {
//用户编号 //用户编号
private Long id; private Long id;
//昵称 //昵称
private String nickname; private String nickname;
//姓名
private String name;
//帐号状态 //帐号状态
private Integer status; // 参见 CommonStatusEnum 枚举 private Integer status; // 参见 CommonStatusEnum 枚举

View File

@@ -0,0 +1,24 @@
package com.tashow.cloud.userapi.enums;
import com.tashow.cloud.common.enums.RpcConstants;
/**
* API 相关的枚举
*
* @author 芋道源码
*/
public class ApiConstants {
/**
* 服务名
*
* 注意,需要保证和 spring.application.name 保持一致
*/
public static final String NAME = "user-server";
public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/user";
public static final String VERSION = "1.0.0";
}

View File

@@ -1,4 +1,4 @@
package com.tashow.cloud.memberapi.enums; package com.tashow.cloud.userapi.enums;
/** /**
* Member 字典类型的枚举类 * Member 字典类型的枚举类

View File

@@ -1,4 +1,4 @@
package com.tashow.cloud.memberapi.enums; package com.tashow.cloud.userapi.enums;
import com.tashow.cloud.common.exception.ErrorCode; import com.tashow.cloud.common.exception.ErrorCode;

View File

@@ -1,4 +1,4 @@
package com.tashow.cloud.memberapi.enums; package com.tashow.cloud.userapi.enums;
import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.EnumUtil;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;

View File

@@ -1,4 +1,4 @@
package com.tashow.cloud.memberapi.enums.point; package com.tashow.cloud.userapi.enums.point;
import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.EnumUtil;
import com.tashow.cloud.common.core.ArrayValuable; import com.tashow.cloud.common.core.ArrayValuable;

View File

@@ -0,0 +1,4 @@
/**
* 消息队列的消息
*/
package com.tashow.cloud.userapi.message;

View File

@@ -1,4 +1,4 @@
package com.tashow.cloud.memberapi.message.user; package com.tashow.cloud.userapi.message.user;
import lombok.Data; import lombok.Data;

View File

@@ -126,6 +126,10 @@
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId> <!-- 文件客户端:文件类型的识别 -->
</dependency>
<dependency> <dependency>

View File

@@ -1,21 +0,0 @@
package com.tashow.cloud.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 文档地址
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum DocumentEnum {
REDIS_INSTALL("https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I4VCSJ", "Redis 安装文档"),
TENANT("https://doc.iocoder.cn", "SaaS 多租户文档");
private final String url;
private final String memo;
}

View File

@@ -26,7 +26,7 @@ public class PageParam implements Serializable {
/** /**
* 页码,从 1 开始", example = "1 * 页码,从 1 开始", example = "1
*/ */
@NotNull(message = "页码不能为空") @NotNull(message = "页码不能为空" )
@Min(value = 1, message = "页码最小值为 1") @Min(value = 1, message = "页码最小值为 1")
private Integer pageNo = 1; private Integer pageNo = 1;

View File

@@ -0,0 +1,50 @@
package com.tashow.cloud.common.serializer;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 图片URL序列化器
* 自动为相对路径的图片添加文件服务器前缀
*/
@Component
public class ImgJsonSerializer extends JsonSerializer<String> {
@Value("${file-server}")
private String fileServer;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (StrUtil.isBlank(value)) {
gen.writeString(StrUtil.EMPTY);
return;
}
String[] imgs = value.split(StrUtil.COMMA);
StringBuilder sb = new StringBuilder();
String resourceUrl = "";
String rule = "^((http[s]{0,1})://)";
Pattern pattern = Pattern.compile(rule);
resourceUrl = fileServer;
for (String img : imgs) {
Matcher matcher = pattern.matcher(img);
//若图片以http或https开头直接返回
if (matcher.find()) {
sb.append(img).append(StrUtil.COMMA);
} else {
sb.append(resourceUrl).append(img).append(StrUtil.COMMA);
}
}
sb.deleteCharAt(sb.length() - 1);
gen.writeString(sb.toString());
}
}

View File

@@ -177,6 +177,15 @@ public class LocalDateTimeUtils {
public static Long between(LocalDateTime dateTime) { public static Long between(LocalDateTime dateTime) {
return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.DAYS); return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.DAYS);
} }
/**
* 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负
*
* @param dateTime 日期
* @return 相差天数
*/
public static Long betweenWithNow(LocalDateTime dateTime) {
return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.SECONDS);
}
/** /**
* 获取今天的开始时间 * 获取今天的开始时间
@@ -196,6 +205,15 @@ public class LocalDateTimeUtils {
return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(1)); return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(1));
} }
/**
* 获取day 天后的日期
*
* @return 昨天
*/
public static LocalDateTime getPlusDay(Integer day) {
return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().plusDays(day));
}
/** /**
* 获取本月的开始时间 * 获取本月的开始时间
* *

View File

@@ -1,4 +1,4 @@
package com.tashow.cloud.infra.framework.file.core.utils; package com.tashow.cloud.common.util.io;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
@@ -64,7 +64,7 @@ public class FileTypeUtils {
String contentType = getMineType(content, filename); String contentType = getMineType(content, filename);
response.setContentType(contentType); response.setContentType(contentType);
// 针对 video 的特殊处理解决视频地址在移动端播放的兼容性问题 // 针对 video 的特殊处理解决视频地址在移动端播放的兼容性问题
if (StrUtil.containsIgnoreCase(contentType, "video")) { if (StrUtil.containsIgnoreCase(contentType, "video")||StrUtil.containsIgnoreCase(contentType, "audio")) {
response.setHeader("Content-Length", String.valueOf(content.length - 1)); response.setHeader("Content-Length", String.valueOf(content.length - 1));
response.setHeader("Content-Range", String.valueOf(content.length - 1)); response.setHeader("Content-Range", String.valueOf(content.length - 1));
response.setHeader("Accept-Ranges", "bytes"); response.setHeader("Accept-Ranges", "bytes");
@@ -72,5 +72,4 @@ public class FileTypeUtils {
// 输出附件 // 输出附件
IoUtil.write(response.getOutputStream(), false, content); IoUtil.write(response.getOutputStream(), false, content);
} }
} }

View File

@@ -8,34 +8,25 @@
* 版权所有侵权必究 * 版权所有侵权必究
*/ */
package com.tashow.cloud.common.util.serializer; package com.tashow.cloud.common.util.json.databind;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* @author lanhai * 文件序列化
*/ */
@Component public class FileJsonSerializer extends JsonSerializer<String> {
public class ImgJsonSerializer extends JsonSerializer<String> {
/* @Autowired
private Qiniu qiniu;
@Autowired
private ImgUploadUtil imgUploadUtil;*/
@Override @Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
/*if (StrUtil.isBlank(value)) { if (StrUtil.isBlank(value)) {
gen.writeString(StrUtil.EMPTY); gen.writeString(StrUtil.EMPTY);
return; return;
} }
@@ -44,11 +35,11 @@ public class ImgJsonSerializer extends JsonSerializer<String> {
String resourceUrl = ""; String resourceUrl = "";
String rule="^((http[s]{0,1})://)"; String rule="^((http[s]{0,1})://)";
Pattern pattern= Pattern.compile(rule); Pattern pattern= Pattern.compile(rule);
if (Objects.equals(imgUploadUtil.getUploadType(), 2)) { // if (Objects.equals(imgUploadUtil.getUploadType(), 2)) {
resourceUrl = qiniu.getResourcesUrl(); // resourceUrl = qiniu.getResourcesUrl();
} else if (Objects.equals(imgUploadUtil.getUploadType(), 1)) { // } else if (Objects.equals(imgUploadUtil.getUploadType(), 1)) {
resourceUrl = imgUploadUtil.getResourceUrl(); // resourceUrl = imgUploadUtil.getResourceUrl();
} // }
for (String img : imgs) { for (String img : imgs) {
Matcher matcher = pattern.matcher(img); Matcher matcher = pattern.matcher(img);
//若图片以http或https开头直接返回 //若图片以http或https开头直接返回
@@ -59,6 +50,6 @@ public class ImgJsonSerializer extends JsonSerializer<String> {
} }
} }
sb.deleteCharAt(sb.length()-1); sb.deleteCharAt(sb.length()-1);
gen.writeString(sb.toString());*/ gen.writeString(sb.toString());
} }
} }

View File

@@ -0,0 +1,27 @@
package com.tashow.cloud.common.util.json.databind;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
/**
* 基于字符串的 LocalDateTime 序列化器
*
* @author 老五
*/
public class StringLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
public static final StringLocalDateTimeSerializer INSTANCE = new StringLocalDateTimeSerializer();
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
// 将 LocalDateTime 对象,转换为 Long 时间戳
gen.writeString(LocalDateTimeUtil.format(value, DatePattern.NORM_DATETIME_FORMATTER) );
}
}

View File

@@ -29,7 +29,7 @@ import java.util.List;
* 1. {@link BaseMapper} 为 MyBatis Plus 的基础接口,提供基础的 CRUD 能力 * 1. {@link BaseMapper} 为 MyBatis Plus 的基础接口,提供基础的 CRUD 能力
* 2. {@link MPJBaseMapper} 为 MyBatis Plus Join 的基础接口,提供连表 Join 能力 * 2. {@link MPJBaseMapper} 为 MyBatis Plus Join 的基础接口,提供连表 Join 能力
*/ */
public interface BaseMapperX<T> extends MPJBaseMapper<T> { public interface BaseMapperX<T> extends MPJBaseMapper<T>,BaseMapper<T> {
default PageResult<T> selectPage(SortablePageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) { default PageResult<T> selectPage(SortablePageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
return selectPage(pageParam, pageParam.getSortingFields(), queryWrapper); return selectPage(pageParam, pageParam.getSortingFields(), queryWrapper);

View File

@@ -33,7 +33,7 @@ public class EnvEnvironmentPostProcessor implements EnvironmentPostProcessor {
environment.getSystemProperties().put(hostNameKey, EnvUtils.getHostName()); environment.getSystemProperties().put(hostNameKey, EnvUtils.getHostName());
} }
// 1.1 如果没有 yudao.env.tag 配置项,则不进行配置项的修改 // 1.1 如果没有 tashow.env.tag 配置项,则不进行配置项的修改
String tag = EnvUtils.getTag(environment); String tag = EnvUtils.getTag(environment);
if (StrUtil.isEmpty(tag)) { if (StrUtil.isEmpty(tag)) {
return; return;

View File

@@ -9,7 +9,7 @@ import jakarta.validation.constraints.NotNull;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ConfigurationProperties(prefix = "yudao.security") @ConfigurationProperties(prefix = "tashow.security")
@Validated @Validated
@Data @Data
public class SecurityProperties { public class SecurityProperties {

View File

@@ -136,9 +136,12 @@ public class WebSecurityConfigurerAdapter {
.requestMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll() .requestMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.HEAD, permitAllUrls.get(HttpMethod.HEAD).toArray(new String[0])).permitAll() .requestMatchers(HttpMethod.HEAD, permitAllUrls.get(HttpMethod.HEAD).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.PATCH, permitAllUrls.get(HttpMethod.PATCH).toArray(new String[0])).permitAll() .requestMatchers(HttpMethod.PATCH, permitAllUrls.get(HttpMethod.PATCH).toArray(new String[0])).permitAll()
// 1.3 基于 yudao.security.permit-all-urls 无需认证 // 1.3 基于 tashow.security.permit-all-urls 无需认证
.requestMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll() .requestMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll()
) )
//app-api 不在security控制权限
.authorizeHttpRequests(c->c.requestMatchers(webProperties.getAppApi().getPrefix()+"/**").permitAll())
// ②:每个项目的自定义规则 // ②:每个项目的自定义规则
.authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c))) .authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c)))
// ③:兜底规则,必须认证 // ③:兜底规则,必须认证

View File

@@ -8,7 +8,6 @@ import com.tashow.cloud.redis.config.TashowCacheProperties;
import com.tashow.cloud.systemapi.api.tenant.TenantApi; import com.tashow.cloud.systemapi.api.tenant.TenantApi;
import com.tashow.cloud.tenant.core.aop.TenantIgnoreAspect; import com.tashow.cloud.tenant.core.aop.TenantIgnoreAspect;
import com.tashow.cloud.tenant.core.db.TenantDatabaseInterceptor; import com.tashow.cloud.tenant.core.db.TenantDatabaseInterceptor;
import com.tashow.cloud.tenant.core.job.TenantJobAspect;
import com.tashow.cloud.tenant.core.mq.rabbitmq.TenantRabbitMQInitializer; import com.tashow.cloud.tenant.core.mq.rabbitmq.TenantRabbitMQInitializer;
import com.tashow.cloud.tenant.core.redis.TenantRedisCacheManager; import com.tashow.cloud.tenant.core.redis.TenantRedisCacheManager;
import com.tashow.cloud.tenant.core.security.TenantSecurityWebFilter; import com.tashow.cloud.tenant.core.security.TenantSecurityWebFilter;
@@ -86,14 +85,6 @@ public class TenantAutoConfiguration {
return registrationBean; return registrationBean;
} }
// ========== Job ==========
@Bean
@ConditionalOnClass(name = "com.xxl.job.core.handler.annotation.XxlJob")
public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) {
return new TenantJobAspect(tenantFrameworkService);
}
@Bean @Bean
@ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate") @ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate")

View File

@@ -2,14 +2,13 @@ package com.tashow.cloud.tenant.config;
import com.tashow.cloud.systemapi.api.tenant.TenantApi; import com.tashow.cloud.systemapi.api.tenant.TenantApi;
import com.tashow.cloud.tenant.core.rpc.TenantRequestInterceptor; import com.tashow.cloud.tenant.core.rpc.TenantRequestInterceptor;
import com.tashow.cloud.tenant.core.rpc.TenantRequestInterceptor;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@AutoConfiguration @AutoConfiguration
@ConditionalOnProperty(prefix = "tashow.tenant", value = "enable", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户 @ConditionalOnProperty(prefix = "tashow.tenant", value = "enable", matchIfMissing = true)
@EnableFeignClients(clients = TenantApi.class) // 主要是引入相关的 API 服务 @EnableFeignClients(clients = TenantApi.class) // 主要是引入相关的 API 服务
public class TenantRpcAutoConfiguration { public class TenantRpcAutoConfiguration {

View File

@@ -1,7 +1,6 @@
package com.tashow.cloud.tenant.core.context; package com.tashow.cloud.tenant.core.context;
import com.alibaba.ttl.TransmittableThreadLocal; import com.alibaba.ttl.TransmittableThreadLocal;
import com.tashow.cloud.common.enums.DocumentEnum;
/** /**
* 多租户上下文 Holder * 多租户上下文 Holder
@@ -37,8 +36,7 @@ public class TenantContextHolder {
public static Long getRequiredTenantId() { public static Long getRequiredTenantId() {
Long tenantId = getTenantId(); Long tenantId = getTenantId();
if (tenantId == null) { if (tenantId == null) {
throw new NullPointerException("TenantContextHolder 不存在租户编号!可参考文档:" throw new NullPointerException("TenantContextHolder 不存在租户编号!");
+ DocumentEnum.TENANT.getUrl());
} }
return tenantId; return tenantId;
} }

View File

@@ -1,14 +0,0 @@
package com.tashow.cloud.tenant.core.job;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 多租户 Job 注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TenantJob {
}

View File

@@ -1,75 +0,0 @@
package com.tashow.cloud.tenant.core.job;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import com.tashow.cloud.common.util.json.JsonUtils;
import com.tashow.cloud.tenant.core.service.TenantFrameworkService;
import com.tashow.cloud.tenant.core.util.TenantUtils;
import com.xxl.job.core.context.XxlJobContext;
import com.xxl.job.core.context.XxlJobHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 多租户 JobHandler AOP
* 任务执行时,会按照租户逐个执行 Job 的逻辑
*
* 注意,需要保证 JobHandler 的幂等性。因为 Job 因为某个租户执行失败重试时,之前执行成功的租户也会再次执行。
*
* @author 芋道源码
*/
@Aspect
@RequiredArgsConstructor
@Slf4j
public class TenantJobAspect {
private final TenantFrameworkService tenantFrameworkService;
@Around("@annotation(tenantJob)")
public void around(ProceedingJoinPoint joinPoint, TenantJob tenantJob) {
// 获得租户列表
List<Long> tenantIds = tenantFrameworkService.getTenantIds();
if (CollUtil.isEmpty(tenantIds)) {
return;
}
// 逐个租户,执行 Job
Map<Long, String> results = new ConcurrentHashMap<>();
AtomicBoolean success = new AtomicBoolean(true); // 标记,是否存在失败的情况
XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext(); // XXL-Job 上下文
tenantIds.parallelStream().forEach(tenantId -> {
// TODO 芋艿:先通过 parallel 实现并行1多个租户是一条执行日志2异常的情况
TenantUtils.execute(tenantId, () -> {
try {
XxlJobContext.setXxlJobContext(xxlJobContext);
// 执行 Job
Object result = joinPoint.proceed();
results.put(tenantId, StrUtil.toStringOrEmpty(result));
} catch (Throwable e) {
results.put(tenantId, ExceptionUtil.getRootCauseMessage(e));
success.set(false);
// 打印异常
XxlJobHelper.log(StrUtil.format("[多租户({}) 执行任务({}),发生异常:{}]",
tenantId, joinPoint.getSignature(), ExceptionUtils.getStackTrace(e)));
}
});
});
// 记录执行结果
if (success.get()) {
XxlJobHelper.handleSuccess(JsonUtils.toJsonString(results));
} else {
XxlJobHelper.handleFail(JsonUtils.toJsonString(results));
}
}
}

View File

@@ -3,7 +3,6 @@ package com.tashow.cloud.web.web.config;
import com.tashow.cloud.common.enums.WebFilterOrderEnum; import com.tashow.cloud.common.enums.WebFilterOrderEnum;
import com.tashow.cloud.infraapi.api.logger.ApiErrorLogApi; import com.tashow.cloud.infraapi.api.logger.ApiErrorLogApi;
import com.tashow.cloud.web.web.core.filter.CacheRequestBodyFilter; import com.tashow.cloud.web.web.core.filter.CacheRequestBodyFilter;
import com.tashow.cloud.web.web.core.filter.DemoFilter;
import com.tashow.cloud.web.web.core.handler.GlobalExceptionHandler; import com.tashow.cloud.web.web.core.handler.GlobalExceptionHandler;
import com.tashow.cloud.web.web.core.handler.GlobalResponseBodyHandler; import com.tashow.cloud.web.web.core.handler.GlobalResponseBodyHandler;
import com.tashow.cloud.web.web.core.util.WebFrameworkUtils; import com.tashow.cloud.web.web.core.util.WebFrameworkUtils;
@@ -12,7 +11,6 @@ import jakarta.servlet.Filter;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateBuilder;
@@ -53,8 +51,10 @@ public class WebAutoConfiguration implements WebMvcConfigurer {
*/ */
private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) { private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) {
AntPathMatcher antPathMatcher = new AntPathMatcher("."); AntPathMatcher antPathMatcher = new AntPathMatcher(".");
configurer.addPathPrefix(api.getPrefix(), clazz -> clazz.isAnnotationPresent(RestController.class) configurer.addPathPrefix(api.getPrefix(),
&& antPathMatcher.match(api.getController(), clazz.getPackage().getName())); // 仅仅匹配 controller 包 clazz -> clazz.isAnnotationPresent(RestController.class)
&& antPathMatcher.match(api.getController(), clazz.getPackage().getName())
); // 仅仅匹配 controller 包
} }
@Bean @Bean
@@ -102,15 +102,6 @@ public class WebAutoConfiguration implements WebMvcConfigurer {
return createFilterBean(new CacheRequestBodyFilter(), WebFilterOrderEnum.REQUEST_BODY_CACHE_FILTER); return createFilterBean(new CacheRequestBodyFilter(), WebFilterOrderEnum.REQUEST_BODY_CACHE_FILTER);
} }
/**
* 创建 DemoFilter Bean演示模式
*/
@Bean
@ConditionalOnProperty(value = "yudao.demo", havingValue = "true")
public FilterRegistrationBean<DemoFilter> demoFilter() {
return createFilterBean(new DemoFilter(), WebFilterOrderEnum.DEMO_FILTER);
}
public static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) { public static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter); FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
bean.setOrder(order); bean.setOrder(order);

View File

@@ -1,35 +0,0 @@
package com.tashow.cloud.web.web.core.filter;
import cn.hutool.core.util.StrUtil;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.util.servlet.ServletUtils;
import com.tashow.cloud.web.web.core.util.WebFrameworkUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
import static com.tashow.cloud.common.exception.enums.GlobalErrorCodeConstants.DEMO_DENY;
/**
* 演示 Filter禁止用户发起写操作避免影响测试数据
*
* @author 芋道源码
*/
public class DemoFilter extends OncePerRequestFilter {
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String method = request.getMethod();
return !StrUtil.equalsAnyIgnoreCase(method, "POST", "PUT", "DELETE") // 写操作时,不进行过滤率
|| WebFrameworkUtils.getLoginUserId(request) == null; // 非登录用户时,不进行过滤
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
// 直接返回 DEMO_DENY 的结果。即,请求不继续
ServletUtils.writeJSON(response, CommonResult.error(DEMO_DENY));
}
}

View File

@@ -22,8 +22,8 @@ import static com.tashow.cloud.web.web.config.WebAutoConfiguration.createFilterB
@AutoConfiguration @AutoConfiguration
@EnableConfigurationProperties(XssProperties.class) @EnableConfigurationProperties(XssProperties.class)
@ConditionalOnProperty(prefix = "yudao.xss", name = "enable", havingValue = "true", matchIfMissing = true) // 设置为 false 禁用 @ConditionalOnProperty(prefix = "tashow.xss", name = "enable", havingValue = "true", matchIfMissing = true) // 设置为 false 禁用
public class YudaoXssAutoConfiguration implements WebMvcConfigurer { public class TashowXssAutoConfiguration implements WebMvcConfigurer {
/** /**
* Xss 清理者 * Xss 清理者

View File

@@ -12,7 +12,7 @@ import java.util.List;
* *
* @author 芋道源码 * @author 芋道源码
*/ */
@ConfigurationProperties(prefix = "yudao.xss") @ConfigurationProperties(prefix = "tashow.xss")
@Validated @Validated
@Data @Data
public class XssProperties { public class XssProperties {

View File

@@ -7,12 +7,16 @@ spring:
username: nacos # Nacos 账号 username: nacos # Nacos 账号
password: nacos # Nacos 密码 password: nacos # Nacos 密码
discovery: # 【配置中心】配置项 discovery: # 【配置中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境 namespace: 63caf548-313d-44bb-929c-531bf2f3b1a2 # 命名空间
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
metadata:
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
config: # 【注册中心】配置项 config: # 【注册中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境 namespace: 63caf548-313d-44bb-929c-531bf2f3b1a2 # 命名空间
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
# 日志文件配置 # 日志文件配置
logging: logging:
level: level:

View File

@@ -18,7 +18,8 @@
<module>tashow-module-ai</module> <module>tashow-module-ai</module>
<module>tashow-module-pay</module> <module>tashow-module-pay</module>
<module>tashow-module-trade</module> <module>tashow-module-trade</module>
<module>tashow-module-member</module> <module>tashow-module-user</module>
<module>tashow-module-file</module>
</modules> </modules>
</project> </project>

View File

@@ -28,6 +28,11 @@
<groupId>com.tashow.cloud</groupId> <groupId>com.tashow.cloud</groupId>
<artifactId>tashow-infra-api</artifactId> <artifactId>tashow-infra-api</artifactId>
</dependency> </dependency>
<!--文件管理 - file-server-->
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-file-api</artifactId>
</dependency>
<!-- RPC 远程调用相关 --> <!-- RPC 远程调用相关 -->
<dependency> <dependency>
<groupId>com.tashow.cloud</groupId> <groupId>com.tashow.cloud</groupId>

View File

@@ -1,20 +1,34 @@
package com.tashow.cloud.ai.controller.admin.aisample; package com.tashow.cloud.ai.controller.admin.aisample;
import com.tashow.cloud.ai.controller.admin.aisample.vo.*; import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleFileRespVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSamplePageReqVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleRelateModelVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleRelateTagVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleRespVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleSaveReqVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.FileUploadReqVO;
import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO; import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO;
import com.tashow.cloud.ai.service.aisample.AiSampleService; import com.tashow.cloud.ai.service.aisample.AiSampleService;
import com.tashow.cloud.common.pojo.CommonResult; import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.pojo.PageResult; import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.common.util.object.BeanUtils;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List; import java.util.List;
import static com.tashow.cloud.common.pojo.CommonResult.success; import static com.tashow.cloud.common.pojo.CommonResult.success;
@@ -31,14 +45,12 @@ public class AiSampleController {
@PostMapping("/create") @PostMapping("/create")
@Operation(summary = "创建样本库-上传文件") @Operation(summary = "创建样本库-上传文件")
@PermitAll @PermitAll
// @PreAuthorize("@ss.hasPermission('ai:sample:create')")
public CommonResult<List<AiSampleFileRespVO>> createAiSample(FileUploadReqVO uploadReqVO) { public CommonResult<List<AiSampleFileRespVO>> createAiSample(FileUploadReqVO uploadReqVO) {
return success(aiSampleService.createAiSample(uploadReqVO)); return success(aiSampleService.createAiSample(uploadReqVO));
} }
@PutMapping("/updates") @PutMapping("/updates")
@Operation(summary = "更新样本") @Operation(summary = "批量更新样本")
// @PreAuthorize("@ss.hasPermission('ai:sample:update')")
@PermitAll @PermitAll
public CommonResult<Boolean> updateAiSample(@Valid @RequestBody List<AiSampleSaveReqVO> updateReqVO) { public CommonResult<Boolean> updateAiSample(@Valid @RequestBody List<AiSampleSaveReqVO> updateReqVO) {
aiSampleService.updateAiSamples(updateReqVO); aiSampleService.updateAiSamples(updateReqVO);
@@ -47,7 +59,6 @@ public class AiSampleController {
@PutMapping("/relate") @PutMapping("/relate")
@Operation(summary = "添加关联标签") @Operation(summary = "添加关联标签")
// @PreAuthorize("@ss.hasPermission('ai:sample:update')")
@PermitAll @PermitAll
public CommonResult<Boolean> relate(@Valid @RequestBody AiSampleRelateTagVO relateTagVO) { public CommonResult<Boolean> relate(@Valid @RequestBody AiSampleRelateTagVO relateTagVO) {
aiSampleService.relate(relateTagVO); aiSampleService.relate(relateTagVO);
@@ -56,7 +67,6 @@ public class AiSampleController {
@DeleteMapping("/deleteRelate") @DeleteMapping("/deleteRelate")
@Operation(summary = "删除关联标签") @Operation(summary = "删除关联标签")
// @PreAuthorize("@ss.hasPermission('ai:sample:delete')")
@PermitAll @PermitAll
public CommonResult<Boolean> deleteRelate(@Valid @RequestBody AiSampleRelateTagVO relateTagVO) { public CommonResult<Boolean> deleteRelate(@Valid @RequestBody AiSampleRelateTagVO relateTagVO) {
aiSampleService.deleteRelate(relateTagVO); aiSampleService.deleteRelate(relateTagVO);
@@ -64,9 +74,8 @@ public class AiSampleController {
} }
@DeleteMapping("/delete") @DeleteMapping("/delete")
@Operation(summary = "删除样本") @Operation(summary = "删除样本")
@Parameter(name = "id", description = "编号", required = true) @Parameter(name = "id", description = "编号", required = true)
// @PreAuthorize("@ss.hasPermission('ai:sample:delete')")
@PermitAll @PermitAll
public CommonResult<Boolean> deleteAiSample(@RequestParam("id") String ids) { public CommonResult<Boolean> deleteAiSample(@RequestParam("id") String ids) {
aiSampleService.deleteAiSample(ids); aiSampleService.deleteAiSample(ids);
@@ -74,21 +83,36 @@ public class AiSampleController {
} }
@GetMapping("/get") @GetMapping("/get")
@Operation(summary = "获得样本") @Operation(summary = "获得样本")
@Parameter(name = "id", description = "编号", required = true, example = "1024") @Parameter(name = "id", description = "编号", required = true, example = "1024")
// @PreAuthorize("@ss.hasPermission('ai:sample:query')")
@PermitAll @PermitAll
public CommonResult<AiSampleRespVO> getAiSample(@RequestParam("id") Long id) { public CommonResult<AiSampleRespVO> getAiSample(@RequestParam("id") Long id) {
AiSampleDO aiSample = aiSampleService.getAiSample(id); return success(aiSampleService.buildSampleRespVO(aiSampleService.getAiSample(id)));
return success(BeanUtils.toBean(aiSample, AiSampleRespVO.class));
} }
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "获得样本分页") @Operation(summary = "获得样本分页")
// @PreAuthorize("@ss.hasPermission('ai:sample:query')")
@PermitAll @PermitAll
public CommonResult<PageResult<AiSampleRespVO>> getAiSamplePage(@Valid AiSamplePageReqVO pageReqVO) { public CommonResult<PageResult<AiSampleRespVO>> getAiSamplePage(@Valid AiSamplePageReqVO pageReqVO) {
PageResult<AiSampleDO> pageResult = aiSampleService.getAiSamplePage(pageReqVO); PageResult<AiSampleDO> pageResult = aiSampleService.getAiSamplePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AiSampleRespVO.class)); List<AiSampleRespVO> list = pageResult.getList().stream().map(aiSampleService::buildSampleRespVO).toList();
return success(new PageResult<>(list, pageResult.getTotal()));
}
@GetMapping("/download")
@Operation(summary = "批量下载样本")
@Parameter(name = "ids", description = "样本ID列表逗号分隔", required = true, example = "1,2,3")
@PermitAll
public void downloadSamples(@RequestParam("ids") String ids, HttpServletResponse response) throws IOException {
aiSampleService.downloadSamples(ids, response);
}
@PutMapping("/relateModel")
@Operation(summary = "样本关联模型")
@PermitAll
public CommonResult<Boolean> relateModel(@Valid @RequestBody AiSampleRelateModelVO relateModelVO) {
aiSampleService.relateSamples(relateModelVO.getSampleIds(), relateModelVO.getModelIds());
return success(true);
} }
} }

View File

@@ -2,6 +2,7 @@ package com.tashow.cloud.ai.controller.admin.aisample;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagListRespVO; import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagListRespVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagPageReqVO; import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagPageReqVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagGroupRespVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagSaveReqVO; import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleTagSaveReqVO;
import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO; import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO;
import com.tashow.cloud.ai.service.aisample.AiSampleTagService; import com.tashow.cloud.ai.service.aisample.AiSampleTagService;
@@ -66,6 +67,14 @@ public class AiSampleTagController {
return success(BeanUtils.toBean(aiSampleTagService.getAiSampleTagList(), AiSampleTagListRespVO.class)); return success(BeanUtils.toBean(aiSampleTagService.getAiSampleTagList(), AiSampleTagListRespVO.class));
} }
@GetMapping("/group-list-by-tag-id")
@Operation(summary = "根据标签 id 获取分组")
@Parameter(name = "tagId", description = "标签 id", required = true)
@PermitAll
public CommonResult<List<AiSampleTagGroupRespVO>> getGroupListByTagId(@RequestParam("tagId") Long tagId) {
return success(BeanUtils.toBean(aiSampleTagService.getGroupListByTagId(tagId), AiSampleTagGroupRespVO.class));
}
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "获得样本标签库分页") @Operation(summary = "获得样本标签库分页")
// @PreAuthorize("@ss.hasPermission('ai:sampleTag:query')") // @PreAuthorize("@ss.hasPermission('ai:sampleTag:query')")

View File

@@ -1,6 +1,8 @@
package com.tashow.cloud.ai.controller.admin.aisample.vo; package com.tashow.cloud.ai.controller.admin.aisample.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.tashow.cloud.common.serializer.ImgJsonSerializer;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@@ -10,8 +12,10 @@ import lombok.Data;
public class AiSampleFileRespVO { public class AiSampleFileRespVO {
@Schema(description = "文件地址") @Schema(description = "文件地址")
@JsonSerialize(using = ImgJsonSerializer.class)
private String fileUrl; private String fileUrl;
@Schema(description = "文件名称") @Schema(description = "文件名称")
private String fileName; private String fileName;
} }

View File

@@ -12,10 +12,10 @@ import lombok.ToString;
@ToString(callSuper = true) @ToString(callSuper = true)
public class AiSamplePageReqVO extends PageParam { public class AiSamplePageReqVO extends PageParam {
@Schema(description = "标签ids", example = "25839") @Schema(description = "标签ids")
private String tagIds; private String tagIds;
@Schema(description = "样本名称", example = "张三") @Schema(description = "样本名称")
private String sampleName; private String sampleName;
@Schema(description = "样本格式", example = "1") @Schema(description = "样本格式", example = "1")

View File

@@ -0,0 +1,18 @@
package com.tashow.cloud.ai.controller.admin.aisample.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 样本关联模型 Request VO")
@Data
public class AiSampleRelateModelVO {
@Schema(description = "样本ID列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Long> sampleIds;
@Schema(description = "模型ID列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Long> modelIds;
}

View File

@@ -1,26 +1,44 @@
package com.tashow.cloud.ai.controller.admin.aisample.vo; package com.tashow.cloud.ai.controller.admin.aisample.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO; import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO;
import com.tashow.cloud.common.serializer.ImgJsonSerializer;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
@Schema(description = "管理后台 - 样本库 Response VO") @Schema(description = "管理后台 - 样本库 Response VO")
@Data @Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ExcelIgnoreUnannotated @ExcelIgnoreUnannotated
public class AiSampleRespVO { public class AiSampleRespVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "7701") @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "7701")
private Long id; private Long id;
@Schema(description = "标签列表") @Schema(description = "标签列表已废弃使用enumTags和customTags代替")
@Deprecated
private List<AiSampleTagDO> tags; private List<AiSampleTagDO> tags;
@Schema(description = "枚举标签列表(包含分组信息)")
private List<TagInfo> enumTags;
// @Schema(description = "个性标签列表")
// private List<TagInfo> customTags;
@Schema(description = "关联模型列表")
private List<ModelInfo> relatedModels;
@Schema(description = "样本文件地址") @Schema(description = "样本文件地址")
@JsonSerialize(using = ImgJsonSerializer.class)
private String sampleFilePath; private String sampleFilePath;
@Schema(description = "样本名称", example = "张三") @Schema(description = "样本名称", example = "张三")
@@ -45,4 +63,46 @@ public class AiSampleRespVO {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime updateTime; private LocalDateTime updateTime;
@Schema(description = "文件后缀")
private String suffix;
@Schema(description = "标签信息")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class TagInfo {
@Schema(description = "标签ID")
private Long id;
@Schema(description = "标签名称")
private String tagName;
@Schema(description = "枚举值")
private String enumValue;
@Schema(description = "分组ID")
private Long groupId;
@Schema(description = "分组名称")
private String groupName;
}
@Schema(description = "模型信息")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ModelInfo {
@Schema(description = "模型ID")
private Long id;
@Schema(description = "模型名称")
private String modelName;
@Schema(description = "版本号")
private String version;
}
} }

View File

@@ -6,7 +6,7 @@ import lombok.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*; import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 样本标签分组库 Response VO") @Schema(description = "管理后台 - 样本标签")
@Data @Data
@ExcelIgnoreUnannotated @ExcelIgnoreUnannotated
public class AiSampleTagGroupRespVO { public class AiSampleTagGroupRespVO {
@@ -19,6 +19,10 @@ public class AiSampleTagGroupRespVO {
@ExcelProperty("分组名称") @ExcelProperty("分组名称")
private String groupName; private String groupName;
@Schema(description = "1-枚举型, 2-个性型", example = "1")
@ExcelProperty("分组类型")
private Integer groupType;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间") @ExcelProperty("创建时间")
private LocalDateTime createTime; private LocalDateTime createTime;

View File

@@ -3,7 +3,7 @@ package com.tashow.cloud.ai.controller.admin.aisample.vo;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
@Schema(description = "管理后台 - 样本标签分组库新增/修改 Request VO") @Schema(description = "管理后台 - 样本标签分组库")
@Data @Data
public class AiSampleTagGroupSaveReqVO { public class AiSampleTagGroupSaveReqVO {
@@ -13,4 +13,10 @@ public class AiSampleTagGroupSaveReqVO {
@Schema(description = "分组名称", example = "张三") @Schema(description = "分组名称", example = "张三")
private String groupName; private String groupName;
@Schema(description = "分组类型1-枚举型, 2-个性型", example = "1")
private Integer groupType;
@Schema(description = "枚举值(枚举分组专用)", example = "")
private String enumValue;
} }

View File

@@ -13,4 +13,8 @@ public class AiSampleTagListRespVO {
private Long id; private Long id;
@Schema(description = "标签名称") @Schema(description = "标签名称")
private String tagName; private String tagName;
@Schema(description = "标签类型1-枚举标签, 2-个性标签")
private Integer tagType;
@Schema(description = "枚举值")
private String enumValue;
} }

View File

@@ -18,4 +18,13 @@ public class AiSampleTagSaveReqVO {
@Schema(description = "标签名称", example = "张三") @Schema(description = "标签名称", example = "张三")
private String tagName; private String tagName;
@Schema(description = "标签类型1-枚举标签, 2-个性标签", example = "1")
private Integer tagType;
@Schema(description = "枚举值", example = "")
private String enumValue;
@Schema(description = "关联分组ID单个ID或逗号分隔的多个ID", example = "1")
private String groupId;
} }

View File

@@ -0,0 +1,128 @@
package com.tashow.cloud.ai.controller.admin.model;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleRespVO;
import com.tashow.cloud.ai.controller.admin.model.vo.AiModelAccuracyAnalysisReqVO;
import com.tashow.cloud.ai.controller.admin.model.vo.AiModelAnalysisResultVO;
import com.tashow.cloud.ai.controller.admin.model.vo.AiModelDifferenceVO;
import com.tashow.cloud.ai.controller.admin.model.vo.AiModelIterateReqVO;
import com.tashow.cloud.ai.controller.admin.model.vo.AiModelPageReqVO;
import com.tashow.cloud.ai.controller.admin.model.vo.AiModelReferenceSamplesReqVO;
import com.tashow.cloud.ai.controller.admin.model.vo.AiModelRespVO;
import com.tashow.cloud.ai.controller.admin.model.vo.AiModelSaveReqVO;
import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO;
import com.tashow.cloud.ai.dal.dataobject.model.AiModelDO;
import com.tashow.cloud.ai.service.aisample.AiSampleService;
import com.tashow.cloud.ai.service.model.AiModelService;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.common.util.object.BeanUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.tashow.cloud.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - AI模型管理")
@RestController
@RequestMapping("/ai/model")
@Validated
public class AiModelController {
@Resource
private AiModelService modelService;
@Resource
private AiSampleService aiSampleService;
@PostMapping("/create")
@Operation(summary = "创建模型")
@PermitAll
public CommonResult<Long> createModel(@Valid @RequestBody AiModelSaveReqVO createReqVO) {
return success(modelService.createModel(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新模型")
@PermitAll
public CommonResult<Boolean> updateModel(@Valid @RequestBody AiModelSaveReqVO updateReqVO) {
modelService.updateModel(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除模型")
@Parameter(name = "id", description = "编号", required = true)
@PermitAll
public CommonResult<Boolean> deleteModel(@RequestParam("id") Long id) {
modelService.deleteModel(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获取所有已启用的模型")
@PermitAll
public CommonResult<List<AiModelRespVO>> getEnabledModels() {
List<AiModelDO> models = modelService.getEnabledModels();
return success(BeanUtils.toBean(models, AiModelRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得模型分页")
@PermitAll
public CommonResult<PageResult<AiModelRespVO>> getModelPage(@Valid AiModelPageReqVO pageReqVO) {
PageResult<AiModelDO> pageResult = modelService.getModelPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AiModelRespVO.class));
}
@PostMapping("/reference-samples")
@Operation(summary = "模型引用样本")
@PermitAll
public CommonResult<Boolean> referenceSamples(@Valid @RequestBody AiModelReferenceSamplesReqVO reqVO) {
modelService.referenceSamples(reqVO.getModelId(), reqVO.getSampleIds());
return success(true);
}
@GetMapping("/samples")
@Operation(summary = "获取模型关联样本")
@Parameter(name = "modelId", description = "模型ID", required = true)
@PermitAll
public CommonResult<List<AiSampleRespVO>> getModelSamples(@RequestParam Long modelId) {
List<AiSampleDO> samples = modelService.getModelSamples(modelId);
return success(samples.stream().map(aiSampleService::buildSampleRespVO).toList());
}
@PostMapping("/accuracy-analysis")
@Operation(summary = "模型准确度分析")
@PermitAll
public CommonResult<AiModelAnalysisResultVO> analyzeModel(@Valid @RequestBody AiModelAccuracyAnalysisReqVO reqVO) {
return success(modelService.analyzeModel(reqVO.getSampleIds(), reqVO.getModelIds()));
}
@GetMapping("/difference")
@Operation(summary = "模型样本差异筛查")
@PermitAll
public CommonResult<AiModelDifferenceVO> checkDifference(@RequestParam List<Long> modelIds) {
return success(modelService.checkDifference(modelIds));
}
@PostMapping("/iterate")
@Operation(summary = "模型迭代")
@PermitAll
public CommonResult<Long> iterateModel(@Valid @RequestBody AiModelIterateReqVO reqVO) {
return success(modelService.iterateModel(reqVO.getModelId(), reqVO.getNewVersion(), reqVO.getDescription()));
}
}

View File

@@ -0,0 +1,21 @@
package com.tashow.cloud.ai.controller.admin.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 模型准确度分析 Request VO")
@Data
public class AiModelAccuracyAnalysisReqVO {
@Schema(description = "样本ID列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "样本ID列表不能为空")
private List<Long> sampleIds;
@Schema(description = "模型ID列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "模型ID列表不能为空")
private List<Long> modelIds;
}

View File

@@ -0,0 +1,68 @@
package com.tashow.cloud.ai.controller.admin.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
@Schema(description = "管理后台 - 模型准确度分析")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiModelAnalysisResultVO {
@Schema(description = "样本分析列表")
private List<SampleAnalysis> samples;
@Schema(description = "样本分析")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SampleAnalysis {
@Schema(description = "样本ID")
private Long sampleId;
@Schema(description = "样本名称", example = "")
private String sampleName;
@Schema(description = "枚举标签", example = "")
private String enumTag;
@Schema(description = "模型分析结果列表")
private List<ModelAnalysisResult> modelResults;
}
@Schema(description = "模型分析结果")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ModelAnalysisResult {
@Schema(description = "模型ID")
private Long modelId;
@Schema(description = "模型名称", example = "A01 模型")
private String modelName;
@Schema(description = "模型版本", example = "V1.0.0")
private String modelVersion;
@Schema(description = "物种识别结果", example = "")
private String speciesResult;
@Schema(description = "物种匹配度", example = "1.00")
private BigDecimal speciesMatchRate;
@Schema(description = "情绪识别结果", example = "兴奋")
private String emotionResult;
@Schema(description = "情绪匹配度", example = "0.95")
private BigDecimal emotionMatchRate;
}
}

View File

@@ -0,0 +1,24 @@
package com.tashow.cloud.ai.controller.admin.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Schema(description = "管理后台 - 模型样本差异")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiModelDifferenceVO {
@Schema(description = "重复引用样本列表")
private List<AiSampleDetailVO> duplicatedSamples;
@Schema(description = "未重复引用样本列表")
private List<AiSampleDetailVO> uniqueSamples;
}

View File

@@ -0,0 +1,23 @@
package com.tashow.cloud.ai.controller.admin.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - 模型迭代 ")
@Data
public class AiModelIterateReqVO {
@Schema(description = "模型ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "模型ID不能为空")
private Long modelId;
@Schema(description = "新版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "v2.0")
@NotBlank(message = "新版本号不能为空")
private String newVersion;
@Schema(description = "版本描述", example = "优化算法性能")
private String description;
}

View File

@@ -0,0 +1,24 @@
package com.tashow.cloud.ai.controller.admin.model.vo;
import com.tashow.cloud.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - AI模型分")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AiModelPageReqVO extends PageParam {
@Schema(description = "模型名称", example = "宠物声音翻译")
private String modelName;
@Schema(description = "版本号", example = "v1.0.0")
private String version;
@Schema(description = "状态0-禁用 1-启用)", example = "1")
private Integer status;
}

View File

@@ -0,0 +1,22 @@
package com.tashow.cloud.ai.controller.admin.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 模型引用样")
@Data
public class AiModelReferenceSamplesReqVO {
@Schema(description = "模型ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "模型ID不能为空")
private Long modelId;
@Schema(description = "样本ID列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "样本ID列表不能为空")
private List<Long> sampleIds;
}

View File

@@ -0,0 +1,40 @@
package com.tashow.cloud.ai.controller.admin.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - AI模型")
@Data
public class AiModelRespVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "模型名称", example = "模型")
private String modelName;
@Schema(description = "版本号", example = "v1.0.0")
private String version;
@Schema(description = "负载", example = "0.75")
private BigDecimal loadPercentage;
@Schema(description = "状态0-禁用 1-启用 2-测试中 3-已废弃)", example = "1")
private Integer status;
@Schema(description = "版本描述", example = "描述")
private String description;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,35 @@
package com.tashow.cloud.ai.controller.admin.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - AI模型")
@Data
public class AiModelSaveReqVO {
@Schema(description = "主键", example = "1")
private Long id;
@Schema(description = "模型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "模型")
@NotBlank(message = "模型名称不能为空")
private String modelName;
@Schema(description = "版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "v1.0.0")
@NotBlank(message = "版本号不能为空")
private String version;
@Schema(description = "负载", example = "0.75")
private BigDecimal loadPercentage;
@Schema(description = "状态0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "版本描述", example = "描述")
private String description;
}

View File

@@ -0,0 +1,80 @@
package com.tashow.cloud.ai.controller.admin.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 样本详情VO - 用于模型分析和差异筛查
*/
@Schema(description = "样本详情VO")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiSampleDetailVO {
@Schema(description = "样本ID")
private Long id;
@Schema(description = "样本名称")
private String sampleName;
@Schema(description = "样本文件地址")
private String sampleFilePath;
@Schema(description = "枚举标签列表")
private List<TagInfo> enumTags;
@Schema(description = "个性标签列表")
private List<TagInfo> customTags;
@Schema(description = "关联模型列表")
private List<ModelInfo> relatedModels;
/**
* 标签信息
*/
@Schema(description = "标签信息")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class TagInfo {
@Schema(description = "标签ID")
private Long id;
@Schema(description = "标签名称")
private String tagName;
@Schema(description = "枚举值")
private String enumValue;
@Schema(description = "分组名称")
private String groupName;
}
/**
* 模型信息
*/
@Schema(description = "模型信息")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ModelInfo {
@Schema(description = "模型ID")
private Long id;
@Schema(description = "模型名称")
private String modelName;
@Schema(description = "版本号")
private String version;
}
}

View File

@@ -6,8 +6,9 @@ import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateReqVo;
import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateRespVo; import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateRespVo;
import com.tashow.cloud.ai.service.dialog.AiDialogService; import com.tashow.cloud.ai.service.dialog.AiDialogService;
import com.tashow.cloud.common.pojo.CommonResult; import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.pojo.PageParam;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -15,8 +16,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.tashow.cloud.common.pojo.CommonResult.success; import static com.tashow.cloud.common.pojo.CommonResult.success;
/** /**
@@ -28,8 +27,6 @@ import static com.tashow.cloud.common.pojo.CommonResult.success;
@Slf4j @Slf4j
public class DialogController { public class DialogController {
List<String> message = List.of("渴了", "饿了", "想睡觉", "想出去玩", "想溜达", "情绪低落", "很开心", "很伤心", "想哭");
@Resource @Resource
private AiDialogService aiDialogService; private AiDialogService aiDialogService;
@@ -38,11 +35,10 @@ public class DialogController {
* 获取对话消息列表 * 获取对话消息列表
*/ */
@GetMapping("/getDialog") @GetMapping("/getDialog")
@PermitAll public CommonResult<DialogResp> msList(@Valid PageParam pageParam) {
public CommonResult<DialogResp> msList() {
//获取当前登录用户 //获取当前登录用户
Long userId = 1L; Long userId = 1L;
return success(aiDialogService.getDialog(userId)); return success(aiDialogService.getDialog(userId,pageParam));
} }
@@ -50,7 +46,6 @@ public class DialogController {
* 翻译 * 翻译
*/ */
@PostMapping("/translate") @PostMapping("/translate")
@PermitAll
public CommonResult<TranslateRespVo> translate(@Validated TranslateReqVo fileReqVo) { public CommonResult<TranslateRespVo> translate(@Validated TranslateReqVo fileReqVo) {
return success(aiDialogService.translate(fileReqVo)); return success(aiDialogService.translate(fileReqVo));
} }

View File

@@ -1,11 +1,11 @@
package com.tashow.cloud.ai.controller.app.dialog.vo; package com.tashow.cloud.ai.controller.app.dialog.vo;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.tashow.cloud.common.util.json.databind.StringLocalDateTimeSerializer;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import java.time.LocalDateTime;
/** /**
* ai-对话消息 DO * ai-对话消息 DO
@@ -69,8 +69,13 @@ public class AiDialogMessageRespVo {
/** /**
* 创建时间 * 创建时间
*/ */
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) //发送时间
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @JsonSerialize(using = StringLocalDateTimeSerializer.class)
private String createTime; private LocalDateTime createTime;
private Integer emotion;
private String terminal;
private String fileType;
private String fileName;
} }

View File

@@ -1,10 +1,9 @@
package com.tashow.cloud.ai.controller.app.dialog.vo; package com.tashow.cloud.ai.controller.app.dialog.vo;
import com.tashow.cloud.common.pojo.PageResult;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.util.List;
@Schema(description = "api - 对话 Response VO") @Schema(description = "api - 对话 Response VO")
@Data @Data
@@ -17,5 +16,5 @@ public class DialogResp {
@Schema(description = "对话状态") @Schema(description = "对话状态")
private String dialogStatus; private String dialogStatus;
@Schema(description = "对话消息内容") @Schema(description = "对话消息内容")
private List<AiDialogMessageRespVo> messages; private PageResult<AiDialogMessageRespVo> messages;
} }

View File

@@ -14,11 +14,13 @@ public class TranslateReqVo {
//消息id //消息id
private Long msgId; private Long msgId;
private String terminal;
/** 文件附件 */ /** 文件附件 */
private MultipartFile file; private MultipartFile file;
//文件时长 //文件时长
private String contentDuration; private Long contentDuration;
} }

View File

@@ -1,11 +1,11 @@
package com.tashow.cloud.ai.controller.app.dialog.vo; package com.tashow.cloud.ai.controller.app.dialog.vo;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.tashow.cloud.common.util.json.databind.StringLocalDateTimeSerializer;
import lombok.Data; import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import java.time.LocalDateTime;
/** /**
* 翻译接口结果vo * 翻译接口结果vo
@@ -39,9 +39,9 @@ public class TranslateRespVo {
private String transResult; private String transResult;
//文件时长 //文件时长
private Long contentDuration; private Long contentDuration;
private String terminal;
//发送时间 //发送时间
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @JsonSerialize(using = StringLocalDateTimeSerializer.class)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime createTime;
private String createTime;
} }

View File

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.tashow.cloud.common.serializer.ImgJsonSerializer;
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO; import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
import lombok.*; import lombok.*;
@@ -57,5 +59,9 @@ public class AiSampleDO extends BaseDO {
* 样本注释 * 样本注释
*/ */
private String remark; private String remark;
/**
*后缀)
*/
private String suffix;
} }

View File

@@ -1,8 +1,8 @@
package com.tashow.cloud.ai.dal.dataobject.aisample; package com.tashow.cloud.ai.dal.dataobject.aisample;
import lombok.*;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO; import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
import lombok.*;
/** /**
* 样本标签库 DO * 样本标签库 DO
@@ -30,5 +30,19 @@ public class AiSampleTagDO extends BaseDO {
*/ */
private String tagName; private String tagName;
/**
* 枚举值
*/
private String enumValue;
/**
* 标签类型1-枚举标签, 2-个性标签
*/
private Integer tagType;
/**
* 关联分组ID单个ID或逗号分隔的多个ID
*/
private String groupId;
} }

View File

@@ -31,4 +31,14 @@ public class AiSampleTagGroupDO extends BaseDO {
*/ */
private String groupName; private String groupName;
/**
* 分组类型1-枚举型, 2-个性型
*/
private Integer groupType;
/**
* 枚举值
*/
private String enumValue;
} }

View File

@@ -1,10 +1,14 @@
package com.tashow.cloud.ai.dal.dataobject.dialog; package com.tashow.cloud.ai.dal.dataobject.dialog;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*; import lombok.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/** /**
* ai-对话消息 DO * ai-对话消息 DO
* *
@@ -89,6 +93,11 @@ public class AiDialogMessageDO {
* 创建时间 * 创建时间
*/ */
@TableField(fill = FieldFill.INSERT) @TableField(fill = FieldFill.INSERT)
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime createTime; private LocalDateTime createTime;
private Integer emotion;
private String terminal;
} }

View File

@@ -0,0 +1,59 @@
package com.tashow.cloud.ai.dal.dataobject.model;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
import lombok.*;
import java.math.BigDecimal;
/**
* AI模型管理 DO
*
* @author tashow
*/
@TableName("tz_ai_model")
@KeySequence("tz_ai_model_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiModelDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 模型名称
*/
private String modelName;
/**
* 版本号
*/
private String version;
/**
* 负载百分比0.0-1.0
*/
private BigDecimal loadPercentage;
/**
* 状态0-禁用 1-启用 2-测试中 3-已废弃)
*/
private Integer status;
/**
* 版本描述
*/
private String description;
}

View File

@@ -0,0 +1,40 @@
package com.tashow.cloud.ai.dal.dataobject.model;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
import lombok.*;
/**
* 模型样本关联 DO
*
* @author tashow
*/
@TableName("tz_ai_model_sample_relate")
@KeySequence("tz_ai_model_sample_relate_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiModelSampleRelateDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 模型ID
*/
private Long modelId;
/**
* 样本ID
*/
private Long sampleId;
}

View File

@@ -0,0 +1,27 @@
package com.tashow.cloud.ai.dal.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum EmotionEnums {
XF(1,"兴奋/玩耍","兴奋"),
PJ(2,"平静/放松","平静"),
je(3,"乞求/饥饿/口渴","饥饿"),
yk(4,"愉快/满足","愉快"),
tt(5,"疼痛/沮丧","疼痛"),
kj(6,"恐惧/不安","恐惧"),
hq(7,"问候/好奇","好奇"),
ng(8,"难过/焦虑","难过"),
fn(9,"愤怒/警告","愤怒"),
;
private Integer code;
private String name;
private String aiName;
}

View File

@@ -28,16 +28,20 @@ public interface AiSampleMapper extends BaseMapperX<AiSampleDO> {
",sample_mine_type sampleMineType ,sample_size sampleSize, remark,creator,updater,create_time createTime" + ",sample_mine_type sampleMineType ,sample_size sampleSize, remark,creator,updater,create_time createTime" +
",update_time updateTime,deleted,tenant_id tenantId " + ",update_time updateTime,deleted,tenant_id tenantId " +
"FROM tz_ai_sample t " + "FROM tz_ai_sample t " +
"INNER JOIN tz_ai_sample_tag_relate r ON t.id = r.sample_id " + "LEFt JOIN tz_ai_sample_tag_relate r ON t.id = r.sample_id " +
"<where>" + "<where>" +
"AND t.deleted =0"+
" <if test=\"pageReqVO.tagIds != null and pageReqVO.tagIds != ''\">" + " <if test=\"pageReqVO.tagIds != null and pageReqVO.tagIds != ''\">" +
" AND FIND_IN_SET(r.sample_tag_id,${pageReqVO.tagIds}) " + " AND FIND_IN_SET(r.sample_tag_id, '${pageReqVO.tagIds}') " +
" </if>" + " </if>" +
" <if test=\"pageReqVO.sampleName != null and pageReqVO.sampleName != ''\">" + " <if test=\"pageReqVO.sampleName != null and pageReqVO.sampleName != ''\">" +
" AND t.sample_name LIKE CONCAT('%',#{pageReqVO.sampleName},'%')" + " AND t.sample_name LIKE CONCAT('%',#{pageReqVO.sampleName},'%')" +
" </if>" + " </if>" +
" <if test=\"pageReqVO.sampleMineType != null and pageReqVO.sampleMineType != ''\">" +
" AND t.sample_mine_type LIKE CONCAT('%',#{pageReqVO.sampleMineType},'%')" +
" </if>" +
"</where>" + "</where>" +
"ORDER BY t.id DESC" + "GROUP BY t.id ORDER BY t.id DESC" +
"</script>") "</script>")
IPage<AiSampleDO> getAiSamplePage(Page<AiSampleDO> objectPage, AiSamplePageReqVO pageReqVO); IPage<AiSampleDO> getAiSamplePage(Page<AiSampleDO> objectPage, AiSamplePageReqVO pageReqVO);
} }

View File

@@ -24,12 +24,12 @@ public interface AiSampleTagMapper extends BaseMapperX<AiSampleTagDO> {
} }
@Select("<script>" + @Select("<script>" +
"SELECT t.id, t.tag_name tagName " + "SELECT t.id, t.tag_name tagName, t.tag_type tagType, t.enum_value enumValue, t.group_id groupId " +
"FROM tz_ai_sample_tag t " + "FROM tz_ai_sample_tag t " +
"INNER JOIN tz_ai_sample_tag_group_relate r ON t.id = r.sample_tag_id " +
"<where>" + "<where>" +
" t.deleted = 0" +
" <if test=\"pageReqVO.groupId != null\">" + " <if test=\"pageReqVO.groupId != null\">" +
" AND r.sample_tag_group_id = #{pageReqVO.groupId}" + " AND (t.group_id IS NULL OR FIND_IN_SET(#{pageReqVO.groupId}, t.group_id))" +
" </if>" + " </if>" +
" <if test=\"pageReqVO.tagName != null and pageReqVO.tagName != ''\">" + " <if test=\"pageReqVO.tagName != null and pageReqVO.tagName != ''\">" +
" AND t.tag_name LIKE CONCAT('%',#{pageReqVO.tagName},'%')" + " AND t.tag_name LIKE CONCAT('%',#{pageReqVO.tagName},'%')" +

View File

@@ -0,0 +1,26 @@
package com.tashow.cloud.ai.dal.mysql.model;
import com.tashow.cloud.ai.controller.admin.model.vo.AiModelPageReqVO;
import com.tashow.cloud.ai.dal.dataobject.model.AiModelDO;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
import org.apache.ibatis.annotations.Mapper;
/**
* AI模型管理 Mapper
*
* @author tashow
*/
@Mapper
public interface AiModelMapper extends BaseMapperX<AiModelDO> {
default PageResult<AiModelDO> selectPage(AiModelPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<AiModelDO>()
.likeIfPresent(AiModelDO::getModelName, reqVO.getModelName())
.eqIfPresent(AiModelDO::getVersion, reqVO.getVersion())
.eqIfPresent(AiModelDO::getStatus, reqVO.getStatus())
.orderByDesc(AiModelDO::getCreateTime));
}
}

View File

@@ -0,0 +1,15 @@
package com.tashow.cloud.ai.dal.mysql.model;
import com.tashow.cloud.ai.dal.dataobject.model.AiModelSampleRelateDO;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
/**
* 模型样本关联 Mapper
*
* @author tashow
*/
@Mapper
public interface AiModelSampleRelateMapper extends BaseMapperX<AiModelSampleRelateDO> {
}

View File

@@ -1,6 +1,6 @@
package com.tashow.cloud.ai.framework.rpc.config; package com.tashow.cloud.ai.framework.rpc.config;
import com.tashow.cloud.infraapi.api.file.FileApi; import com.tashow.cloud.fileapi.api.file.FileApi;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;

View File

@@ -32,8 +32,6 @@ public class SecurityConfiguration {
.requestMatchers(adminSeverContextPath + "/**").permitAll(); .requestMatchers(adminSeverContextPath + "/**").permitAll();
// 文件读取 // 文件读取
registry.requestMatchers(buildAdminApi("/infra/file/*/get/**")).permitAll(); registry.requestMatchers(buildAdminApi("/infra/file/*/get/**")).permitAll();
// TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案
// RPC 服务的安全配置 // RPC 服务的安全配置
registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
} }

View File

@@ -3,8 +3,10 @@ package com.tashow.cloud.ai.service.aisample;
import com.tashow.cloud.ai.controller.admin.aisample.vo.*; import com.tashow.cloud.ai.controller.admin.aisample.vo.*;
import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO; import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO;
import com.tashow.cloud.common.pojo.PageResult; import com.tashow.cloud.common.pojo.PageResult;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import java.io.IOException;
import java.util.List; import java.util.List;
/** /**
@@ -69,5 +71,28 @@ public interface AiSampleService {
*/ */
PageResult<AiSampleDO> getAiSamplePage(AiSamplePageReqVO pageReqVO); PageResult<AiSampleDO> getAiSamplePage(AiSamplePageReqVO pageReqVO);
/**
* 批量下载样本
*
* @param ids 样本ID列表逗号分隔
* @param response HTTP响应
*/
void downloadSamples(String ids, HttpServletResponse response) throws IOException;
/**
* 样本关联模型(覆写逻辑)
*
* @param sampleIds 样本ID列表
* @param modelIds 模型ID列表
*/
void relateSamples(List<Long> sampleIds, List<Long> modelIds);
/**
* 构建样本响应VO包含枚举标签、个性标签、关联模型
*
* @param sample 样本DO
* @return 样本响应VO
*/
AiSampleRespVO buildSampleRespVO(AiSampleDO sample);
} }

View File

@@ -1,37 +1,52 @@
package com.tashow.cloud.ai.service.aisample; package com.tashow.cloud.ai.service.aisample;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.tashow.cloud.ai.controller.admin.aisample.vo.*; import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleFileRespVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSamplePageReqVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleRelateTagVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleRespVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleSaveReqVO;
import com.tashow.cloud.ai.controller.admin.aisample.vo.FileUploadReqVO;
import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO; import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleDO;
import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO; import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagDO;
import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagGroupDO;
import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagRelateDO; import com.tashow.cloud.ai.dal.dataobject.aisample.AiSampleTagRelateDO;
import com.tashow.cloud.ai.dal.dataobject.model.AiModelSampleRelateDO;
import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleMapper; import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleMapper;
import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleTagGroupMapper;
import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleTagMapper; import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleTagMapper;
import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleTagRelateMapper; import com.tashow.cloud.ai.dal.mysql.aisample.AiSampleTagRelateMapper;
import com.tashow.cloud.ai.dal.mysql.model.AiModelMapper;
import com.tashow.cloud.ai.dal.mysql.model.AiModelSampleRelateMapper;
import com.tashow.cloud.common.pojo.PageResult; import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.common.util.object.BeanUtils; import com.tashow.cloud.common.util.object.BeanUtils;
import com.tashow.cloud.infraapi.api.file.FileApi; import com.tashow.cloud.fileapi.api.file.FileApi;
import com.tashow.cloud.mybatis.mybatis.core.util.MyBatisUtils; import com.tashow.cloud.mybatis.mybatis.core.util.MyBatisUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 样本库 Service 实现类
*
* @author 芋道源码
*/
@Service @Service
@Validated @Validated
public class AiSampleServiceImpl implements AiSampleService { public class AiSampleServiceImpl implements AiSampleService {
@@ -43,39 +58,41 @@ public class AiSampleServiceImpl implements AiSampleService {
@Resource @Resource
private AiSampleTagRelateMapper aiSampleTagRelateMapper; private AiSampleTagRelateMapper aiSampleTagRelateMapper;
@Resource @Resource
private AiSampleTagGroupMapper aiSampleTagGroupMapper;
@Resource
private AiModelSampleRelateMapper modelSampleRelateMapper;
@Resource
private AiModelMapper modelMapper;
@Resource
private FileApi fileApi; private FileApi fileApi;
@Value("file-server")
@Value("${file-server}")
private String fileServer; private String fileServer;
@Override @Override
@SneakyThrows @SneakyThrows
public List<AiSampleFileRespVO> createAiSample(FileUploadReqVO uploadReqVO) { public List<AiSampleFileRespVO> createAiSample(FileUploadReqVO uploadReqVO) {
//返回图片路径 List<AiSampleFileRespVO> result = new ArrayList<>();
List<AiSampleFileRespVO> urls = new ArrayList<>();
/* 调用文件上传服务*/
for (MultipartFile file : uploadReqVO.getFiles()) { for (MultipartFile file : uploadReqVO.getFiles()) {
//返回上传结果 String filename = file.getOriginalFilename();
String file1 = fileServer + fileApi.createFile(file.getBytes()); String filePath = fileApi.createFile(filename, "", file.getBytes());
//保存样本信息
AiSampleDO aiSampleDO = new AiSampleDO(); AiSampleDO sample = new AiSampleDO();
aiSampleDO.setSampleFilePath(file1); sample.setSampleFilePath(filePath);
aiSampleDO.setSampleName(file.getOriginalFilename()); sample.setSampleName(filename.substring(0, filename.lastIndexOf('.')));
aiSampleDO.setSampleMineType(file.getContentType()); sample.setSuffix(filename.substring(filename.lastIndexOf('.') + 1));
aiSampleDO.setSampleSize(file.getSize()); sample.setSampleMineType(file.getContentType());
aiSampleMapper.insert(aiSampleDO); sample.setSampleSize(file.getSize());
urls.add(new AiSampleFileRespVO().setFileUrl(file1).setFileName(file.getOriginalFilename())); aiSampleMapper.insert(sample);
result.add(new AiSampleFileRespVO().setFileUrl(filePath).setFileName(filename));
} }
// 返回 return result;
return urls;
} }
@Override @Override
public void updateAiSample(AiSampleSaveReqVO updateReqVO) { public void updateAiSample(AiSampleSaveReqVO updateReqVO) {
// 校验存在 aiSampleMapper.updateById(BeanUtils.toBean(updateReqVO, AiSampleDO.class));
validateAiSampleExists(updateReqVO.getId());
// 更新
AiSampleDO updateObj = BeanUtils.toBean(updateReqVO, AiSampleDO.class);
aiSampleMapper.updateById(updateObj);
} }
@Override @Override
@@ -85,96 +102,232 @@ public class AiSampleServiceImpl implements AiSampleService {
@Override @Override
public void relate(AiSampleRelateTagVO relateTagVO) { public void relate(AiSampleRelateTagVO relateTagVO) {
List<AiSampleTagRelateDO> tagRelateDOS = new ArrayList<>(); List<AiSampleTagRelateDO> relates = new ArrayList<>();
for (Long sampleId : relateTagVO.getSampleIds()) { for (Long sampleId : relateTagVO.getSampleIds()) {
for (Long tagId : relateTagVO.getTagId()) { for (Long tagId : relateTagVO.getTagId()) {
AiSampleTagRelateDO relateDO = aiSampleTagRelateMapper.selectOne( validateTagGroupRule(sampleId, tagId);
new LambdaQueryWrapper<AiSampleTagRelateDO>() AiSampleTagRelateDO exist = aiSampleTagRelateMapper.selectOne(new LambdaQueryWrapper<AiSampleTagRelateDO>()
.eq(AiSampleTagRelateDO::getSampleId, sampleId) .eq(AiSampleTagRelateDO::getSampleId, sampleId)
.eq(AiSampleTagRelateDO::getSampleTagId, tagId) ); .eq(AiSampleTagRelateDO::getSampleTagId, tagId));
if (relateDO== null){ if (exist == null) {
relateDO = new AiSampleTagRelateDO(); relates.add(new AiSampleTagRelateDO().setSampleId(sampleId).setSampleTagId(tagId));
relateDO.setSampleId(sampleId);
relateDO.setSampleTagId(tagId);
tagRelateDOS.add(relateDO);
} }
} }
} }
if (!tagRelateDOS.isEmpty()){ if (CollUtil.isNotEmpty(relates)) {
aiSampleTagRelateMapper.insertBatch(tagRelateDOS); aiSampleTagRelateMapper.insertBatch(relates);
} }
} }
@Override @Override
public void deleteRelate(AiSampleRelateTagVO relateTagVO) { public void deleteRelate(AiSampleRelateTagVO relateTagVO) {
List<Long> tagRelateIds = new ArrayList<>(); List<Long> ids = new ArrayList<>();
for (Long sampleId : relateTagVO.getSampleIds()) { for (Long sampleId : relateTagVO.getSampleIds()) {
for (Long tagId : relateTagVO.getTagId()) { for (Long tagId : relateTagVO.getTagId()) {
AiSampleTagRelateDO relateDO = aiSampleTagRelateMapper.selectOne( AiSampleTagRelateDO exist = aiSampleTagRelateMapper.selectOne(new LambdaQueryWrapper<AiSampleTagRelateDO>()
new LambdaQueryWrapper<AiSampleTagRelateDO>()
.eq(AiSampleTagRelateDO::getSampleId, sampleId) .eq(AiSampleTagRelateDO::getSampleId, sampleId)
.eq(AiSampleTagRelateDO::getSampleTagId, tagId) .eq(AiSampleTagRelateDO::getSampleTagId, tagId));
); if (exist != null) {
if (relateDO != null) { ids.add(exist.getId());
tagRelateIds.add(relateDO.getId());
} }
} }
} }
if (!tagRelateIds.isEmpty()) { if (CollUtil.isNotEmpty(ids)) {
aiSampleTagRelateMapper.deleteBatchIds(tagRelateIds); aiSampleTagRelateMapper.deleteBatchIds(ids);
} }
} }
@Override @Override
public void deleteAiSample(String ids) { public void deleteAiSample(String ids) {
// 删除
aiSampleMapper.deleteByIds(Arrays.asList(ids.split(StrUtil.COMMA))); aiSampleMapper.deleteByIds(Arrays.asList(ids.split(StrUtil.COMMA)));
} }
private void validateAiSampleExists(Long id) {
if (aiSampleMapper.selectById(id) == null) {
// throw exception(AI_SAMPLE_NOT_EXISTS);
}
}
@Override @Override
public AiSampleDO getAiSample(Long id) { public AiSampleDO getAiSample(Long id) {
AiSampleDO aiSampleDO = aiSampleMapper.selectById(id); AiSampleDO sample = aiSampleMapper.selectById(id);
aiSampleDO.setSampleFilePath(aiSampleDO.getSampleFilePath()); if (sample == null) {
//先获取关联的标签id return null;
List<AiSampleTagRelateDO> tagRelateDOS = aiSampleTagRelateMapper.selectList(new LambdaQueryWrapper<AiSampleTagRelateDO>().eq(AiSampleTagRelateDO::getSampleId, id)); }
List<Long> tagIds = tagRelateDOS.stream().map(AiSampleTagRelateDO::getSampleTagId).toList();
aiSampleDO.setTags(aiSampleTagMapper.selectList(new LambdaQueryWrapper<AiSampleTagDO>().in(AiSampleTagDO::getId, tagIds))); List<Long> tagIds = aiSampleTagRelateMapper.selectList(new LambdaQueryWrapper<AiSampleTagRelateDO>()
return aiSampleDO; .eq(AiSampleTagRelateDO::getSampleId, id))
.stream().map(AiSampleTagRelateDO::getSampleTagId).toList();
sample.setTags(CollUtil.isEmpty(tagIds)
? List.of()
: aiSampleTagMapper.selectList(new LambdaQueryWrapper<AiSampleTagDO>().in(AiSampleTagDO::getId, tagIds)));
return sample;
} }
@Override @Override
public PageResult<AiSampleDO> getAiSamplePage(AiSamplePageReqVO pageReqVO) { public PageResult<AiSampleDO> getAiSamplePage(AiSamplePageReqVO pageReqVO) {
// PageResult<AiSampleDO> aiSampleDOPageResult = aiSampleMapper.selectPage(pageReqVO); IPage<AiSampleDO> page = aiSampleMapper.getAiSamplePage(MyBatisUtils.buildPage(pageReqVO), pageReqVO);
IPage<AiSampleDO> aiSampleDOPageResult = aiSampleMapper.getAiSamplePage(MyBatisUtils.buildPage(pageReqVO),pageReqVO); List<AiSampleDO> samples = page.getRecords();
//根据样本id获取关联的标签id List<Long> sampleIds = samples.stream().map(AiSampleDO::getId).toList();
List<Long> sampleIds = aiSampleDOPageResult.getRecords().stream().map(AiSampleDO::getId).toList(); if (CollUtil.isEmpty(sampleIds)) {
List<AiSampleTagRelateDO> tagRelateDOS = aiSampleTagRelateMapper.selectList( return new PageResult<>(samples, page.getTotal());
new LambdaQueryWrapper<AiSampleTagRelateDO>() }
.in(!sampleIds.isEmpty(), AiSampleTagRelateDO::getSampleId, sampleIds));
List<Long> tagIds = tagRelateDOS.stream().map(AiSampleTagRelateDO::getSampleTagId).toList();
//获取标签信息
List<AiSampleTagDO> aiSampleTagDOS = aiSampleTagMapper.selectList(
new LambdaQueryWrapper<AiSampleTagDO>()
.in(!tagIds.isEmpty(), AiSampleTagDO::getId, tagIds));
//封装标签信息 List<AiSampleTagRelateDO> relates = aiSampleTagRelateMapper.selectList(new LambdaQueryWrapper<AiSampleTagRelateDO>()
for (AiSampleDO aiSampleDO : aiSampleDOPageResult.getRecords()) { .in(AiSampleTagRelateDO::getSampleId, sampleIds));
List<AiSampleTagRelateDO> list = tagRelateDOS.stream() List<Long> tagIds = relates.stream().map(AiSampleTagRelateDO::getSampleTagId).distinct().toList();
.filter(a -> ObjectUtil.equals(aiSampleDO.getId(), a.getSampleId())) List<AiSampleTagDO> tags = CollUtil.isEmpty(tagIds)
? List.of()
: aiSampleTagMapper.selectList(new LambdaQueryWrapper<AiSampleTagDO>().in(AiSampleTagDO::getId, tagIds));
Map<Long, AiSampleTagDO> tagMap = new HashMap<>();
for (AiSampleTagDO tag : tags) {
tagMap.put(tag.getId(), tag);
}
Map<Long, List<AiSampleTagDO>> sampleTagMap = new HashMap<>();
for (AiSampleTagRelateDO relate : relates) {
AiSampleTagDO tag = tagMap.get(relate.getSampleTagId());
if (tag != null) {
sampleTagMap.computeIfAbsent(relate.getSampleId(), k -> new ArrayList<>()).add(tag);
}
}
for (AiSampleDO sample : samples) {
sample.setTags(sampleTagMap.getOrDefault(sample.getId(), List.of()));
}
return new PageResult<>(samples, page.getTotal());
}
@Override
public void downloadSamples(String ids, HttpServletResponse response) throws IOException {
List<AiSampleDO> samples = aiSampleMapper.selectBatchIds(Arrays.asList(ids.split(StrUtil.COMMA)));
if (samples.size() == 1) {
AiSampleDO sample = samples.get(0);
byte[] bytes = HttpUtil.downloadBytes(fileServer + sample.getSampleFilePath());
response.setContentType(sample.getSampleMineType());
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(sample.getSampleName(), "UTF-8"));
response.getOutputStream().write(bytes);
return;
}
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=samples.zip");
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
for (AiSampleDO sample : samples) {
byte[] bytes = HttpUtil.downloadBytes(fileServer + sample.getSampleFilePath());
zos.putNextEntry(new ZipEntry(sample.getSampleName()));
zos.write(bytes);
zos.closeEntry();
}
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void relateSamples(List<Long> sampleIds, List<Long> modelIds) {
if (CollUtil.isEmpty(sampleIds)) {
return;
}
boolean noModels = CollUtil.isEmpty(modelIds);
for (Long sampleId : sampleIds) {
modelSampleRelateMapper.delete(new LambdaQueryWrapper<AiModelSampleRelateDO>()
.eq(AiModelSampleRelateDO::getSampleId, sampleId));
if (noModels) {
continue;
}
for (Long modelId : modelIds) {
modelSampleRelateMapper.insert(new AiModelSampleRelateDO().setSampleId(sampleId).setModelId(modelId));
}
}
}
private void validateTagGroupRule(Long sampleId, Long tagId) {
AiSampleTagDO currentTag = aiSampleTagMapper.selectById(tagId);
if (currentTag == null || !Integer.valueOf(1).equals(currentTag.getTagType())) {
return;
}
Long groupId = getFirstGroupId(currentTag.getGroupId());
if (groupId == null) {
return;
}
AiSampleTagGroupDO group = aiSampleTagGroupMapper.selectById(groupId);
if (group != null && Integer.valueOf(2).equals(group.getGroupType())) {
return;
}
List<AiSampleTagRelateDO> existingRelates = aiSampleTagRelateMapper.selectList(new LambdaQueryWrapper<AiSampleTagRelateDO>()
.eq(AiSampleTagRelateDO::getSampleId, sampleId));
for (AiSampleTagRelateDO relate : existingRelates) {
if (tagId.equals(relate.getSampleTagId())) {
continue;
}
AiSampleTagDO existingTag = aiSampleTagMapper.selectById(relate.getSampleTagId());
if (existingTag != null && parseGroupIds(existingTag.getGroupId()).contains(groupId)) {
aiSampleTagRelateMapper.deleteById(relate.getId());
}
}
}
@Override
public AiSampleRespVO buildSampleRespVO(AiSampleDO sample) {
AiSampleRespVO respVO = BeanUtils.toBean(sample, AiSampleRespVO.class);
respVO.setEnumTags(buildEnumTags(sample.getId()));
respVO.setRelatedModels(buildRelatedModels(sample.getId()));
return respVO;
}
private List<AiSampleRespVO.TagInfo> buildEnumTags(Long sampleId) {
List<Long> tagIds = aiSampleTagRelateMapper.selectList(new LambdaQueryWrapper<AiSampleTagRelateDO>()
.eq(AiSampleTagRelateDO::getSampleId, sampleId))
.stream().map(AiSampleTagRelateDO::getSampleTagId).toList();
if (CollUtil.isEmpty(tagIds)) {
return List.of();
}
List<AiSampleTagDO> tags = aiSampleTagMapper.selectBatchIds(tagIds).stream()
.filter(tag -> Integer.valueOf(1).equals(tag.getTagType())).toList();
Map<Long, String> groupNames = new HashMap<>();
Set<Long> groupIds = new HashSet<>();
for (AiSampleTagDO tag : tags) {
Long groupId = getFirstGroupId(tag.getGroupId());
if (groupId != null) {
groupIds.add(groupId);
}
}
for (AiSampleTagGroupDO group : aiSampleTagGroupMapper.selectBatchIds(groupIds)) {
groupNames.put(group.getId(), group.getGroupName());
}
return tags.stream().map(tag -> {
Long groupId = getFirstGroupId(tag.getGroupId());
return AiSampleRespVO.TagInfo.builder()
.id(tag.getId())
.tagName(tag.getTagName())
.enumValue(tag.getEnumValue())
.groupId(groupId)
.groupName(groupId == null ? null : groupNames.get(groupId))
.build();
}).toList();
}
private List<AiSampleRespVO.ModelInfo> buildRelatedModels(Long sampleId) {
List<Long> modelIds = modelSampleRelateMapper.selectList(new LambdaQueryWrapper<AiModelSampleRelateDO>()
.eq(AiModelSampleRelateDO::getSampleId, sampleId))
.stream().map(AiModelSampleRelateDO::getModelId).toList();
if (CollUtil.isEmpty(modelIds)) {
return List.of();
}
return modelMapper.selectBatchIds(modelIds).stream()
.map(model -> AiSampleRespVO.ModelInfo.builder()
.id(model.getId())
.modelName(model.getModelName())
.version(model.getVersion())
.build())
.toList(); .toList();
Object[] tagsId = list.stream().map(AiSampleTagRelateDO::getSampleTagId).toArray();
List<AiSampleTagDO> list1 = aiSampleTagDOS.stream().filter(a -> ArrayUtil.containsAny(tagsId, a.getId())).toList();
aiSampleDO.setTags(list1);
aiSampleDO.setSampleFilePath(aiSampleDO.getSampleFilePath());
}
return new PageResult<>(aiSampleDOPageResult.getRecords(), aiSampleDOPageResult.getTotal());
} }
private Long getFirstGroupId(String groupIds) {
return Long.parseLong(groupIds.split(StrUtil.COMMA)[0].trim());
}
private List<Long> parseGroupIds(String groupIds) {
return Arrays.stream(groupIds.split(StrUtil.COMMA))
.map(String::trim)
.map(Long::parseLong)
.toList();
}
} }

View File

@@ -13,50 +13,42 @@ import org.springframework.validation.annotation.Validated;
import java.util.List; import java.util.List;
/**
* 样本标签分组库 Service 实现类
*
* @author 芋道源码
*/
@Service @Service
@Validated @Validated
public class AiSampleTagGroupServiceImpl implements AiSampleTagGroupService { public class AiSampleTagGroupServiceImpl implements AiSampleTagGroupService {
private static final Integer ENUM_TYPE = 1;
@Resource @Resource
private AiSampleTagGroupMapper aiSampleTagGroupMapper; private AiSampleTagGroupMapper aiSampleTagGroupMapper;
@Override @Override
public Long createAiSampleTagGroup(AiSampleTagGroupSaveReqVO createReqVO) { public Long createAiSampleTagGroup(AiSampleTagGroupSaveReqVO createReqVO) {
// 插入 AiSampleTagGroupDO group = BeanUtils.toBean(createReqVO, AiSampleTagGroupDO.class);
AiSampleTagGroupDO aiSampleTagGroup = BeanUtils.toBean(createReqVO, AiSampleTagGroupDO.class); aiSampleTagGroupMapper.insert(group);
aiSampleTagGroupMapper.insert(aiSampleTagGroup); return group.getId();
// 返回
return aiSampleTagGroup.getId();
} }
@Override @Override
public void updateAiSampleTagGroup(AiSampleTagGroupSaveReqVO updateReqVO) { public void updateAiSampleTagGroup(AiSampleTagGroupSaveReqVO updateReqVO) {
// 校验存在 AiSampleTagGroupDO existingGroup = aiSampleTagGroupMapper.selectById(updateReqVO.getId());
validateAiSampleTagGroupExists(updateReqVO.getId()); if (isEnumType(existingGroup.getGroupType())
// 更新 && !existingGroup.getGroupType().equals(updateReqVO.getGroupType())) {
AiSampleTagGroupDO updateObj = BeanUtils.toBean(updateReqVO, AiSampleTagGroupDO.class); throw new IllegalArgumentException("Enum group type cannot be changed");
aiSampleTagGroupMapper.updateById(updateObj); }
AiSampleTagGroupDO group = BeanUtils.toBean(updateReqVO, AiSampleTagGroupDO.class);
aiSampleTagGroupMapper.updateById(group);
} }
@Override @Override
public void deleteAiSampleTagGroup(Long id) { public void deleteAiSampleTagGroup(Long id) {
// 校验存在 AiSampleTagGroupDO group = aiSampleTagGroupMapper.selectById(id);
validateAiSampleTagGroupExists(id); if (isEnumType(group.getGroupType())) {
// 删除 throw new IllegalArgumentException("Enum group cannot be deleted");
}
aiSampleTagGroupMapper.deleteById(id); aiSampleTagGroupMapper.deleteById(id);
} }
private void validateAiSampleTagGroupExists(Long id) {
if (aiSampleTagGroupMapper.selectById(id) == null) {
// throw exception(AI_SAMPLE_TAG_GROUP_NOT_EXISTS);
}
}
@Override @Override
public AiSampleTagGroupDO getAiSampleTagGroup(Long id) { public AiSampleTagGroupDO getAiSampleTagGroup(Long id) {
return aiSampleTagGroupMapper.selectById(id); return aiSampleTagGroupMapper.selectById(id);
@@ -69,6 +61,11 @@ public class AiSampleTagGroupServiceImpl implements AiSampleTagGroupService {
@Override @Override
public List<AiSampleTagGroupDO> getAiSampleTagGroupList() { public List<AiSampleTagGroupDO> getAiSampleTagGroupList() {
return aiSampleTagGroupMapper.selectList(new LambdaQueryWrapper<AiSampleTagGroupDO>().orderByDesc(AiSampleTagGroupDO::getId)); return aiSampleTagGroupMapper.selectList(new LambdaQueryWrapper<AiSampleTagGroupDO>()
.orderByDesc(AiSampleTagGroupDO::getId));
}
private boolean isEnumType(Integer type) {
return ENUM_TYPE.equals(type);
} }
} }

Some files were not shown because too many files have changed in this diff Show More