18 Commits

Author SHA1 Message Date
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
167 changed files with 2543 additions and 2082 deletions

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

@@ -307,4 +307,20 @@ CREATE TABLE `tz_trade_delivery_pick_up_store`
) ENGINE = InnoDB
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

@@ -193,6 +193,11 @@
<artifactId>tashow-sdk-payment</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-product-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- Spring 核心 -->
<dependency>

View File

@@ -0,0 +1,66 @@
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.PutMapping;
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 扣减数量
*/
@GetMapping(PREFIX + "/reduceStocks")
CommonResult<Boolean> reduceStocks(Long skuId, Integer stocksNum) ;
/**
* 增加库存
* @param skuId 单品ID
* @param stocksNum 增加数量
*/
@GetMapping(PREFIX + "/increaseStocks")
CommonResult<Boolean> increaseStocks(Long skuId, Integer stocksNum);
}

View File

@@ -70,6 +70,10 @@ public class ProdListVO {
* 还剩多少天
*/
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_SERVICE_TRANSPORT_NOT_EXISTS = new ErrorCode(10022, "服务遗体运输不存在");
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

@@ -16,6 +16,7 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_ITEM_NOT_FOUND = new ErrorCode(1_011_000_010, "交易订单项不存在");
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_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_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, "交易订单更新支付状态失败,支付单状态不是【支付成功】状态");

View File

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

View File

@@ -17,17 +17,15 @@ import java.util.Arrays;
@Getter
public enum TradeOrderStatusEnum implements ArrayValuable<Integer> {
UNPAID(0, "待支付"),
UNDELIVERED(10, "待发货"),
DELIVERED(20, "已发货"),
COMPLETED(30, "已完成"),
CANCELED(40, "取消"),
WAITPAID(100, "等待付"),
WAITCONFIRM(110, "等待确定"),
WAITSERVE(120, "等待服务"),
WAITACCEPT(130, "等待验收"),
SERVECANCELED(140, "取消服务");
WAITPAID(10, "等待付款"),
WAITCONFIRM(20, "等待确定"),
WAITSERVE(30, "等待服务"),
WAITACCEPT(40, "等待验收"),
COMPLETED(50, "完成"),
CANCELED(60, "已取消"),
DELIVERED(70, "已退"),
WAITDELIVERED(80, "待发货"),
;
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 是否
*/
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 是否
*/
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 是否
*/
public static boolean havePaid(Integer status) {
return ObjectUtils.equalsAny(status, UNDELIVERED.getStatus(),
return ObjectUtils.equalsAny(status, WAITCONFIRM.getStatus(),
DELIVERED.getStatus(), COMPLETED.getStatus());
}

View File

@@ -196,6 +196,15 @@ public class LocalDateTimeUtils {
return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(1));
}
/**
* 获取day 天后的日期
*
* @return 昨天
*/
public static LocalDateTime getPlusDay(Integer day) {
return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().plusDays(day));
}
/**
* 获取本月的开始时间
*

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 能力
* 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) {
return selectPage(pageParam, pageParam.getSortingFields(), queryWrapper);

View File

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

View File

@@ -136,7 +136,7 @@ public class WebSecurityConfigurerAdapter {
.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.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()
)
// ②:每个项目的自定义规则

View File

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

View File

@@ -6,6 +6,7 @@ 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.service.dialog.AiDialogService;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.pojo.PageParam;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import lombok.extern.slf4j.Slf4j;
@@ -15,8 +16,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.tashow.cloud.common.pojo.CommonResult.success;
/**
@@ -28,8 +27,6 @@ import static com.tashow.cloud.common.pojo.CommonResult.success;
@Slf4j
public class DialogController {
List<String> message = List.of("渴了", "饿了", "想睡觉", "想出去玩", "想溜达", "情绪低落", "很开心", "很伤心", "想哭");
@Resource
private AiDialogService aiDialogService;
@@ -39,10 +36,10 @@ public class DialogController {
*/
@GetMapping("/getDialog")
@PermitAll
public CommonResult<DialogResp> msList() {
public CommonResult<DialogResp> msList(PageParam pageParam) {
//获取当前登录用户
Long userId = 1L;
return success(aiDialogService.getDialog(userId));
return success(aiDialogService.getDialog(userId,pageParam));
}

View File

@@ -1,11 +1,11 @@
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 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
@@ -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)
private String createTime;
//发送时间
@JsonSerialize(using = StringLocalDateTimeSerializer.class)
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;
import com.tashow.cloud.common.pojo.PageResult;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "api - 对话 Response VO")
@Data
@@ -17,5 +16,5 @@ public class DialogResp {
@Schema(description = "对话状态")
private String dialogStatus;
@Schema(description = "对话消息内容")
private List<AiDialogMessageRespVo> messages;
private PageResult<AiDialogMessageRespVo> messages;
}

View File

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

View File

@@ -1,11 +1,11 @@
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 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
@@ -39,9 +39,9 @@ public class TranslateRespVo {
private String transResult;
//文件时长
private Long contentDuration;
private String terminal;
//发送时间
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private String createTime;
@JsonSerialize(using = StringLocalDateTimeSerializer.class)
private LocalDateTime createTime;
}

View File

@@ -1,10 +1,14 @@
package com.tashow.cloud.ai.dal.dataobject.dialog;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* ai-对话消息 DO
*
@@ -89,6 +93,11 @@ public class AiDialogMessageDO {
* 创建时间
*/
@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 Integer emotion;
private String terminal;
}

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

@@ -5,6 +5,7 @@ import com.tashow.cloud.ai.controller.app.dialog.vo.DialogResp;
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.dal.dataobject.dialog.AiDialogDO;
import com.tashow.cloud.common.pojo.PageParam;
/**
* ai-对话 Service 接口
@@ -14,7 +15,7 @@ import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogDO;
public interface AiDialogService extends IService<AiDialogDO> {
DialogResp getDialog(Long userId);
DialogResp getDialog(Long userId, PageParam pageParam);
TranslateRespVo translate(TranslateReqVo fileReqVo);
}

View File

@@ -1,5 +1,7 @@
package com.tashow.cloud.ai.service.dialog;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
@@ -15,6 +17,8 @@ import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogDO;
import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogMessageDO;
import com.tashow.cloud.ai.dal.mysql.dialog.AiDialogMapper;
import com.tashow.cloud.ai.dal.mysql.dialog.AiDialogMessageMapper;
import com.tashow.cloud.common.pojo.PageParam;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.common.util.object.BeanUtils;
import com.tashow.cloud.infraapi.api.file.FileApi;
import jakarta.annotation.Resource;
@@ -27,11 +31,8 @@ import org.springframework.web.multipart.MultipartFile;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -54,9 +55,65 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
@Resource
private FileApi fileApi;
private List<String> failedStr =List.of(
"刚刚的音符太独特了,我没听清呢~可以再对我说一次吗?",
"哇,这是哪来的小可爱?声音太有魅力了,让我一时走了神。请靠近一点,慢慢再说一遍好?",
"背景音有点热闹我有点分心啦能在一个更安静的地方让我专心听听TA的声音吗",
"报告主人!声波受到不明干扰(可能是零食袋的声音?),翻译任务失败,请求二次输入!",
"我的‘物种雷达’刚才打了个盹儿~快让我再听一次这美妙的声音!",
"这声音太迷人了让我CPU过载了请简化一下环境音我们再来一次"
);
private Map<String,List<String>> failedMap =new HashMap<>(){{
put("cat",List.of(
"听得出是猫猫在说话但TA似乎在表达一种很深邃的情绪..也许是个小秘密呢?",
"喵星人的这段‘加密通话’等级太高了!翻译官正在努力学习中...你能通过TA的尾巴和眼神猜猜看吗",
"警报!警报!接收到喵星最高机密指令,我的权限不足,无法破译!建议提供小鱼干以获取更多线索"
));
put("dog",List.of(
"听得出是狗狗在说话但TA似乎在表达一种很深邃的情绪...也许是个小秘密呢?",
"汪语十级考试失败!这句大概是“今天的我比昨天更爱你了'之类的复杂情感吧",
"汪星人的这段‘加密通话’等级太高了!翻译官正在努力学习中..你能通过TA的尾巴和眼神猜猜看吗"
));
put("common",List.of(
"TA刚才可能用了某种古老的咒语或者...只是在打一个很有思想的哈欠?",
"这句的哲学深度超出了我的理解范围TA可能是一位诗人"
));
}};
private Map<String,List<String>> successMap =new HashMap<>(){{
put("舒服",List.of(
"TA 现在全身心都放松下来啦,正被满满的安全感包围着呢~ ",
"听这均匀的呼吸TA 的心正像云朵一样软绵绵、轻飘飘的哦。",
"这是幸福的声音此刻的TA觉得自己是全世界最被爱的小宝贝",
"环境很安心主人很贴心TA 正在小声说:好舒服呀~…"
));
put("等待喂食",List.of(
"小肚肚发出‘咕咕’警报啦!美食雷达正在全面启动,搜寻好吃的!",
"来自TA的紧急通讯报告能量储备严重不足请求立刻补充",
"你听到了吗那是小饭碗在思念食物的声音TA的眼神正在发出渴望的光波~",
"“开饭铃”已经摇响!再不开饭,小委屈就要溢出来啦!"
));
put("生气",List.of(
"TA 现在有点小紧张哦周围可能有让TA不安的东西快给TA一点安慰吧",
"检测到心跳加速模式TA需要一点空间和一个温柔的声音来平静下来",
"TA 现在有点小紧张哦周围可能有让TA不安的东西快给TA一点安慰吧"
));
}};
private Map<String, String> petAvatars = new HashMap<>() {{
put("cat", "https://petshy.tashowz.com/admin-api/infra/file/29/get/默认的猫.png");
put("dog", "https://petshy.tashowz.com/admin-api/infra/file/29/get/默认的狗.png");
}};
private Map<String, String> petName = new HashMap<>() {{
put("cat", "猫猫・三胖");
put("dog", "狗狗・旺财");
}};
@Override
public DialogResp getDialog(Long userId) {
public DialogResp getDialog(Long userId, PageParam pageParam) {
AiDialogDO aiDialogDO = this.getOne(new LambdaQueryWrapper<AiDialogDO>().eq(AiDialogDO::getUserId, userId));
if (aiDialogDO == null) {
aiDialogDO = new AiDialogDO();
@@ -65,7 +122,12 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
aiDialogDO.setUserId(userId);
this.save(aiDialogDO);
}
List<AiDialogMessageDO> messageDOS = aiDialogMessageMapper.selectList(new LambdaQueryWrapper<AiDialogMessageDO>().eq(AiDialogMessageDO::getDialogId, aiDialogDO.getId()));
PageResult<AiDialogMessageDO> messageDOS = aiDialogMessageMapper.selectPage(pageParam,
new LambdaQueryWrapper<AiDialogMessageDO>().eq(AiDialogMessageDO::getDialogId, aiDialogDO.getId())
.orderByAsc(AiDialogMessageDO::getCreateTime)
);
DialogResp resp = new DialogResp();
resp.setDialogId(aiDialogDO.getId());
resp.setTitle(aiDialogDO.getTitle());
@@ -77,43 +139,23 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
@Override
@SneakyThrows
public TranslateRespVo translate(TranslateReqVo fileReqVo) {
AiDialogMessageDO messageDO = aiDialogMessageMapper.selectById(fileReqVo.getMsgId());
if (messageDO == null) {
messageDO = new AiDialogMessageDO();
}
String fileName = StrUtil.isBlank(messageDO.getFileName()) ? fileReqVo.getFile().getOriginalFilename() : messageDO.getFileName();
AiDialogMessageDO messageDO = new AiDialogMessageDO();
String fileName = fileReqVo.getFile().getOriginalFilename();
//上传文件获取文件地址
String fileUrl = StrUtil.isBlank(messageDO.getContentText()) ? fileServer + fileApi.createFile(fileName, "", fileReqVo.getFile().getBytes()) : messageDO.getContentText();
String fileUrl = fileServer + fileApi.createFile(fileName, "", fileReqVo.getFile().getBytes());
//翻译结果
translate(messageDO, fileUrl, fileName);
//创建消息 持久化消息
if (messageDO.getId() == null) {
messageDO.setFileName(fileName);
messageDO.setFileType(fileReqVo.getFile().getContentType());
messageDO.setDialogId(fileReqVo.getDialogId());
//前端无法转换时 后端进行转
messageDO.setContentDuration(
StrUtil.isBlank(fileReqVo.getContentDuration()) || "Infinity".equals(fileReqVo.getContentDuration())
? 0 : Long.parseLong(fileReqVo.getContentDuration())
);
messageDO.setContentText(fileUrl);
messageDO.setContentType(2);
//获取当前最后的排序
AiDialogMessageDO aiDialogMessageDO = aiDialogMessageMapper.selectOne(new LambdaQueryWrapper<AiDialogMessageDO>()
.eq(AiDialogMessageDO::getDialogId, fileReqVo.getDialogId())
.orderByDesc(AiDialogMessageDO::getMessageOrder)
.last("limit 1")
);
messageDO.setMessageOrder(aiDialogMessageDO == null ? 1 : aiDialogMessageDO.getMessageOrder() + 1);
messageDO.setCreateTime(LocalDateTime.now());
}
int i = messageDO.getId() == null ? aiDialogMessageMapper.insert(messageDO) : aiDialogMessageMapper.updateById(messageDO);
TranslateRespVo bean = BeanUtils.toBean(messageDO, TranslateRespVo.class);
bean.setId(messageDO.getId());
return bean;
//创建消息
messageDO.setFileName(fileName);
messageDO.setFileType(fileReqVo.getFile().getContentType());
messageDO.setDialogId(fileReqVo.getDialogId());
//前端无法转换时 后端进行转
messageDO.setContentDuration(fileReqVo.getContentDuration());
messageDO.setContentText(fileUrl);
messageDO.setContentType(2);
messageDO.setCreateTime(LocalDateTimeUtil.now());
return BeanUtils.toBean(messageDO, TranslateRespVo.class);
}
@SneakyThrows
@@ -136,39 +178,54 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
//数据解析
JSONObject translateResult = JSON.parseObject(result);
if (translateResult.isEmpty() || !translateResult.containsKey("intent_result") || !translateResult.getBoolean("is_species_sound")) {
//翻译失败
if (translateResult.isEmpty()
|| !translateResult.containsKey("intent_result")
|| !translateResult.getBoolean("is_species_sound")
|| translateResult.getDouble("confidence") < 0.7
) {
messageDO.setTransStatus(0);
messageDO.setTransResult("");
messageDO.setTransResult(failedStr.get(RandomUtil.randomInt(failedStr.size())));
return messageDO;
}
//标签 如 cat dog
String speciesLabels = translateResult.getString("species_labels");
//解析翻译结果
JSONObject probabilities = translateResult.getJSONObject("intent_result")
.getJSONObject("probabilities");
JSONObject intentResult = translateResult.getJSONObject("intent_result");
//宠物档案 todo
messageDO.setPetId(1l);
messageDO.setPetName(petName.get(speciesLabels));
messageDO.setPetAvatar(petAvatars.get(speciesLabels));
messageDO.setPetType(speciesLabels);
//识别物种,无法翻译音频
if (intentResult.getDouble("confidence")<0.7){
messageDO.setTransStatus(0);
List<String> petFailResult = failedMap.get(speciesLabels);
if (petFailResult == null) {
petFailResult = failedMap.get("common");
}
messageDO.setTransResult(petFailResult.get(RandomUtil.randomInt(petFailResult.size())));
return messageDO;
}
//都识别成功后 也有不同的翻译结果
JSONObject probabilities = intentResult.getJSONObject("probabilities");
String resultKey = probabilities.entrySet().stream()
.filter(entry -> entry.getValue() instanceof Number)
.max(Comparator.comparingDouble(entry -> ((Number) entry.getValue()).doubleValue()))
.map(Map.Entry::getKey)
.orElse(null);
//返回结果
//宠物档案 todo
messageDO.setPetId(1l);
messageDO.setPetName(speciesLabels);
messageDO.setPetAvatar("https://img1.baidu.com/it/u=1224902049,3440357835&fm=253&app=138&f=JPEG?w=801&h=800");
messageDO.setPetType(speciesLabels);
//根据key解析结果
String emo = StrUtil.isBlank(resultKey) ? "" : resultKey.split(cn.hutool.core.util.StrUtil.UNDERLINE)[1];
List<String> emoList = successMap.get(emo);
messageDO.setTransStatus(1);
messageDO.setTransResult(StrUtil.isBlank(resultKey) ? "" : resultKey.split(StrUtil.UNDERLINE)[1]);
messageDO.setTransResult(emoList.get(RandomUtil.randomInt(emoList.size())));
return messageDO;
}
public static void main(String[] args) throws Exception {
InputStream inputStream = getInputStreamFromUrl("https://petshy.tashowz.com/admin-api/infra/file/29/get/857def513547ec33a105f71108c8ece329cb64dacc3a4779c94b0fcc3398cc32.webm");
AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);
System.out.println((long) (audioStream.getFrameLength() / audioStream.getFormat().getFrameRate()));
// byte[] bytes = HttpUtil.downloadBytes("http://192.168.1.231:48080/admin-api/infra/file/29/get/c7351abf780f18600c104ec5662d843783ed8c309c01fb427046565217f51102.wav");
//
//// File file = new File("http://192.168.1.231:48080/admin-api/infra/file/29/get/c7351abf780f18600c104ec5662d843783ed8c309c01fb427046565217f51102.wav");
@@ -178,19 +235,4 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
}
public static InputStream getInputStreamFromUrl(String urlString) throws Exception {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET"); // 可以是GET, POST等
connection.setConnectTimeout(5000); // 设置连接超时时间
connection.setReadTimeout(5000); // 设置读取超时时间
int responseCode = connection.getResponseCode(); // 获取响应码
if (responseCode == HttpURLConnection.HTTP_OK) { // 状态码200表示成功
return connection.getInputStream();
} else {
// 处理错误情况例如抛出异常或返回null等
throw new RuntimeException("Failed : HTTP error code : " + responseCode);
}
}
}

View File

@@ -25,6 +25,14 @@
<artifactId>tashow-framework-env</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jaudiotagger/jaudiotagger -->
<dependency>
<groupId>org.jaudiotagger</groupId>
<artifactId>jaudiotagger</artifactId>
<version>2.0.1</version>
</dependency>
<!-- 依赖服务 -->
<dependency>
<groupId>com.tashow.cloud</groupId>

View File

@@ -29,14 +29,13 @@ import static com.tashow.cloud.infra.framework.file.core.utils.FileTypeUtils.wri
@RequestMapping("/infra/file")
@Validated
@Slf4j
@PermitAll
public class FileController {
@Resource private FileService fileService;
/** 上传文件", description = "模式一:后端上传文件 */
@PostMapping("/upload")
public CommonResult<String> uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
public CommonResult<String> uploadFile(@Valid FileUploadReqVO uploadReqVO) throws Exception {
MultipartFile file = uploadReqVO.getFile();
String path = uploadReqVO.getPath();
return success(
@@ -88,7 +87,6 @@ public class FileController {
response.setStatus(HttpStatus.NOT_FOUND.value());
return;
}
response.setContentLength(content.length);
writeAttachment(response, path, content);
}

View File

@@ -6,8 +6,20 @@ import com.alibaba.ttl.TransmittableThreadLocal;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.apache.tika.Tika;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.CannotWriteException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.TagException;
import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
/**
@@ -64,7 +76,7 @@ public class FileTypeUtils {
String contentType = getMineType(content, filename);
response.setContentType(contentType);
// 针对 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-Range", String.valueOf(content.length - 1));
response.setHeader("Accept-Ranges", "bytes");
@@ -73,4 +85,21 @@ public class FileTypeUtils {
IoUtil.write(response.getOutputStream(), false, content);
}
public static void main(String[] args) throws CannotReadException, TagException, InvalidAudioFrameException, ReadOnlyFileException, IOException, CannotWriteException, URISyntaxException {
URL url = new URL("https://petshy.tashowz.com/admin-api/infra/file/29/get/jna2-雪球-难过焦虑.wav");
File file = new File(url.getFile());
System.out.println(file.exists());
AudioFile audioFile = AudioFileIO.read(file);
Tag tag = audioFile.getTag();
if (tag instanceof AbstractID3v2Tag) {
AbstractID3v2Tag id3v2Tag = (AbstractID3v2Tag) tag;
// id3v2Tag.delete(); // 删除所有ID3标签
} else {
System.out.println("The file does not contain ID3v2 tags.");
}
AudioFileIO.write(audioFile); // 保存更改
System.out.println("ID3 tags removed successfully.");
}
}

View File

@@ -1,4 +1,4 @@
-- 将该建表 SQL 语句,添加到 yudao-module-${table.moduleName}-biz 模块的 test/resources/sql/create_tables.sql 文件里
-- 将该建表 SQL 语句,添加到 yudao-module-${table.moduleName}-biz 模块的 test/resources/sql/create_tableslogin.sql 文件里
CREATE TABLE IF NOT EXISTS "${table.tableName.toLowerCase()}" (
#foreach ($column in $columns)
#if (${column.javaType} == 'Long')
@@ -33,5 +33,5 @@ CREATE TABLE IF NOT EXISTS "${table.tableName.toLowerCase()}" (
PRIMARY KEY ("${primaryColumn.columnName.toLowerCase()}")
) COMMENT '${table.tableComment}';
-- 将该删表 SQL 语句,添加到 yudao-module-${table.moduleName}-biz 模块的 test/resources/sql/clean.sql 文件里
-- 将该删表 SQL 语句,添加到 yudao-module-${table.moduleName}-biz 模块的 test/resources/sql/cleanlogin.sql 文件里
DELETE FROM "${table.tableName}";

View File

@@ -1,35 +0,0 @@
package com.tashow.cloud.member.address;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.memberapi.api.address.MemberAddressApi;
import com.tashow.cloud.memberapi.api.address.dto.MemberAddressRespDTO;
import com.tashow.cloud.member.convert.address.AddressConvert;
import com.tashow.cloud.member.service.address.AddressService;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import static com.tashow.cloud.common.pojo.CommonResult.success;
/**
* 用户收件地址 API 实现类
*/
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class MemberAddressApiImpl implements MemberAddressApi {
@Resource
private AddressService addressService;
@Override
public CommonResult<MemberAddressRespDTO> getAddress(Long id, Long userId) {
return success(AddressConvert.INSTANCE.convert02(addressService.getAddress(userId, id)));
}
@Override
public CommonResult<MemberAddressRespDTO> getDefaultAddress(Long userId) {
return success(AddressConvert.INSTANCE.convert02(addressService.getDefaultUserAddress(userId)));
}
}

View File

@@ -1,39 +0,0 @@
package com.tashow.cloud.member.controller.admin.address;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.member.controller.admin.address.vo.AddressRespVO;
import com.tashow.cloud.member.convert.address.AddressConvert;
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
import com.tashow.cloud.member.service.address.AddressService;
import jakarta.annotation.Resource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
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;
// 管理后台 - 用户收件地址
@RestController
@RequestMapping("/member/address")
@Validated
public class AddressController {
@Resource
private AddressService addressService;
@GetMapping("/list")
// 获得用户收件地址列表
// userId: 用户编号,必填
@PreAuthorize("@ss.hasPermission('member:user:query')")
public CommonResult<List<AddressRespVO>> getAddressList(@RequestParam("userId") Long userId) {
List<MemberAddressDO> list = addressService.getAddressList(userId);
return success(AddressConvert.INSTANCE.convertList2(list));
}
}

View File

@@ -1 +0,0 @@
package com.tashow.cloud.member.controller.admin.address;

View File

@@ -1,36 +0,0 @@
package com.tashow.cloud.member.controller.admin.address.vo;
import lombok.*;
import java.time.LocalDateTime;
import java.util.*;
import jakarta.validation.constraints.*;
/**
* 用户收件地址 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class AddressBaseVO {
// 收件人名称,必填,示例:张三
@NotNull(message = "收件人名称不能为空")
private String name;
// 手机号,必填
@NotNull(message = "手机号不能为空")
private String mobile;
// 地区编码必填示例15716
@NotNull(message = "地区编码不能为空")
private Long areaId;
// 收件详细地址,必填
@NotNull(message = "收件详细地址不能为空")
private String detailAddress;
// 是否默认必填示例2
@NotNull(message = "是否默认不能为空")
private Boolean defaultStatus;
}

View File

@@ -1,18 +0,0 @@
package com.tashow.cloud.member.controller.admin.address.vo;
import lombok.*;
import java.time.LocalDateTime;
// 管理后台 - 用户收件地址 Response VO
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AddressRespVO extends AddressBaseVO {
// 收件地址编号必填示例7380
private Long id;
// 创建时间,必填
private LocalDateTime createTime;
}

View File

@@ -1,65 +0,0 @@
package com.tashow.cloud.member.controller.admin.user.vo;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import org.springframework.format.annotation.DateTimeFormat;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
/**
* 会员用户 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class MemberUserBaseVO {
// 手机号必填示例15601691300
@NotNull(message = "手机号不能为空")
private String mobile;
// 状态必填示例2
@NotNull(message = "状态不能为空")
private Byte status;
// 用户昵称,必填,示例:李四
@NotNull(message = "用户昵称不能为空")
private String nickname;
// 头像必填示例https://www.iocoder.cn/x.png
@URL(message = "头像必须是 URL 格式")
private String avatar;
// 用户昵称,示例:李四
private String name;
// 用户性别示例1
private Integer sex;
// 所在地编号示例4371
private Long areaId;
// 所在地全程,示例:上海上海市普陀区
private String areaName;
// 出生日期示例2023-03-12
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDateTime birthday;
// 会员备注,示例:我是小备注
private String mark;
// 会员标签,示例:[1, 2]
private List<Long> tagIds;
// 会员等级编号示例1
private Long levelId;
// 用户分组编号示例1
private Long groupId;
}

View File

@@ -1,48 +0,0 @@
package com.tashow.cloud.member.controller.admin.user.vo;
import com.tashow.cloud.common.pojo.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.List;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// 管理后台 - 会员用户分页 Request VO
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberUserPageReqVO extends PageParam {
// 手机号示例15601691300
private String mobile;
// 用户昵称,示例:李四
private String nickname;
// 最后登录时间
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] loginDate;
// 创建时间
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
// 会员标签编号列表,示例:[1, 2]
private List<Long> tagIds;
// 会员等级编号示例1
private Long levelId;
// 用户分组编号示例1
private Long groupId;
// TODO 芋艿:注册用户类型;
// TODO 芋艿:登录用户类型;
}

View File

@@ -1,51 +0,0 @@
package com.tashow.cloud.member.controller.admin.user.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
import java.util.List;
// 管理后台 - 会员用户 Response VO
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberUserRespVO extends MemberUserBaseVO {
// 编号必填示例23788
private Long id;
// 注册 IP必填示例127.0.0.1
private String registerIp;
// 最后登录IP必填示例127.0.0.1
private String loginIp;
// 最后登录时间,必填
private LocalDateTime loginDate;
// 创建时间,必填
private LocalDateTime createTime;
// ========== 其它信息 ==========
// 积分必填示例100
private Integer point;
// 总积分必填示例2000
private Integer totalPoint;
// 会员标签,示例:[红色, 快乐]
private List<String> tagNames;
// 会员等级,示例:黄金会员
private String levelName;
// 用户分组,示例:购物达人
private String groupName;
// 用户经验值必填示例200
private Integer experience;
}

View File

@@ -1,54 +0,0 @@
### 请求 /create 接口 => 成功
POST {{appApi}}//member/address/create
Content-Type: application/json
tenant-id: {{appTenantId}}
Authorization: Bearer {{appToken}}
{
"name": "yunai",
"mobile": "15601691300",
"areaId": "610632",
"postCode": "200000",
"detailAddress": "芋道源码 233 号 666 室",
"defaulted": true
}
### 请求 /update 接口 => 成功
PUT {{appApi}}//member/address/update
Content-Type: application/json
tenant-id: {{appTenantId}}
Authorization: Bearer {{appToken}}
{
"id": "1",
"name": "yunai888",
"mobile": "15601691300",
"areaId": "610632",
"postCode": "200000",
"detailAddress": "芋道源码 233 号 666 室",
"defaulted": false
}
### 请求 /delete 接口 => 成功
DELETE {{appApi}}//member/address/delete?id=2
Content-Type: application/json
tenant-id: {{appTenantId}}
Authorization: Bearer {{appToken}}
### 请求 /get 接口 => 成功
GET {{appApi}}//member/address/get?id=1
Content-Type: application/json
tenant-id: {{appTenantId}}
Authorization: Bearer {{appToken}}
### 请求 /get-default 接口 => 成功
GET {{appApi}}//member/address/get-default
Content-Type: application/json
tenant-id: {{appTenantId}}
Authorization: Bearer {{appToken}}
### 请求 /list 接口 => 成功
GET {{appApi}}//member/address/list
Content-Type: application/json
tenant-id: {{appTenantId}}
Authorization: Bearer {{appToken}}

View File

@@ -1,95 +0,0 @@
package com.tashow.cloud.member.controller.app.address;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.member.controller.app.address.vo.AppAddressCreateReqVO;
import com.tashow.cloud.member.controller.app.address.vo.AppAddressRespVO;
import com.tashow.cloud.member.controller.app.address.vo.AppAddressUpdateReqVO;
import com.tashow.cloud.member.convert.address.AddressConvert;
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
import com.tashow.cloud.member.service.address.AddressService;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static com.tashow.cloud.common.pojo.CommonResult.success;
import static com.tashow.cloud.security.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* 用户 APP - 用户收件地址
*/
@RestController
@RequestMapping("/member/address")
@Validated
public class AppAddressController {
@Resource
private AddressService addressService;
/**
* 创建用户收件地址
* @param createReqVO
* @return
*/
@PostMapping("/create")
public CommonResult<Long> createAddress(@Valid @RequestBody AppAddressCreateReqVO createReqVO) {
return success(addressService.createAddress(getLoginUserId(), createReqVO));
}
/**
* 更新用户收件地址
* @param updateReqVO
* @return
*/
@PutMapping("/update")
public CommonResult<Boolean> updateAddress(@Valid @RequestBody AppAddressUpdateReqVO updateReqVO) {
addressService.updateAddress(getLoginUserId(), updateReqVO);
return success(true);
}
/**
* 删除用户收件地址
* @param id 编号
* @return
*/
@DeleteMapping("/delete")
public CommonResult<Boolean> deleteAddress(@RequestParam("id") Long id) {
addressService.deleteAddress(getLoginUserId(), id);
return success(true);
}
/**
* 获得用户收件地址
* @param id 编号
* @return
*/
@GetMapping("/get")
public CommonResult<AppAddressRespVO> getAddress(@RequestParam("id") Long id) {
MemberAddressDO address = addressService.getAddress(getLoginUserId(), id);
return success(AddressConvert.INSTANCE.convert(address));
}
/**
* 获得默认的用户收件地址
* @return
*/
@GetMapping("/get-default")
public CommonResult<AppAddressRespVO> getDefaultUserAddress() {
MemberAddressDO address = addressService.getDefaultUserAddress(getLoginUserId());
return success(AddressConvert.INSTANCE.convert(address));
}
/**
* 获得用户收件地址列表
* @return
*/
@GetMapping("/list")
public CommonResult<List<AppAddressRespVO>> getAddressList() {
List<MemberAddressDO> list = addressService.getAddressList(getLoginUserId());
return success(AddressConvert.INSTANCE.convertList(list));
}
}

View File

@@ -1,32 +0,0 @@
package com.tashow.cloud.member.controller.app.address.vo;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
/**
* 用户收件地址 Base VO提供给添加、修改、详细的子 VO 使用
*/
@Data
public class AppAddressBaseVO {
//收件人名称
@NotNull(message = "收件人名称不能为空")
private String name;
//手机号
@NotNull(message = "手机号不能为空")
private String mobile;
//地区编号
@NotNull(message = "地区编号不能为空")
private Long areaId;
//收件详细地址
@NotNull(message = "收件详细地址不能为空")
private String detailAddress;
//是否默认地址
@NotNull(message = "是否默认地址不能为空")
private Boolean defaultStatus;
}

View File

@@ -1,15 +0,0 @@
package com.tashow.cloud.member.controller.app.address.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 用户 APP - 用户收件地址创建 Request VO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppAddressCreateReqVO extends AppAddressBaseVO {
}

View File

@@ -1,21 +0,0 @@
package com.tashow.cloud.member.controller.app.address.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 用户 APP - 用户收件地址 Response VO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppAddressRespVO extends AppAddressBaseVO {
//编号
private Long id;
//地区名字
private String areaName;
}

View File

@@ -1,20 +0,0 @@
package com.tashow.cloud.member.controller.app.address.vo;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 用户 APP - 用户收件地址更新 Request VO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppAddressUpdateReqVO extends AppAddressBaseVO {
//编号
@NotNull(message = "编号不能为空")
private Long id;
}

View File

@@ -1,45 +0,0 @@
package com.tashow.cloud.member.convert.address;
import com.tashow.cloud.common.util.ip.AreaUtils;
import com.tashow.cloud.memberapi.api.address.dto.MemberAddressRespDTO;
import com.tashow.cloud.member.controller.admin.address.vo.AddressRespVO;
import com.tashow.cloud.member.controller.app.address.vo.AppAddressCreateReqVO;
import com.tashow.cloud.member.controller.app.address.vo.AppAddressRespVO;
import com.tashow.cloud.member.controller.app.address.vo.AppAddressUpdateReqVO;
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 用户收件地址 Convert
*
* @author 芋道源码
*/
@Mapper
public interface AddressConvert {
AddressConvert INSTANCE = Mappers.getMapper(AddressConvert.class);
MemberAddressDO convert(AppAddressCreateReqVO bean);
MemberAddressDO convert(AppAddressUpdateReqVO bean);
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
AppAddressRespVO convert(MemberAddressDO bean);
List<AppAddressRespVO> convertList(List<MemberAddressDO> list);
MemberAddressRespDTO convert02(MemberAddressDO bean);
@Named("convertAreaIdToAreaName")
default String convertAreaIdToAreaName(Integer areaId) {
return AreaUtils.format(areaId);
}
List<AddressRespVO> convertList2(List<MemberAddressDO> list);
}

View File

@@ -1,41 +0,0 @@
package com.tashow.cloud.member.convert.user;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.memberapi.api.user.dto.MemberUserRespDTO;
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserRespVO;
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserUpdateReqVO;
import com.tashow.cloud.member.controller.app.user.vo.AppMemberUserInfoRespVO;
import com.tashow.cloud.member.convert.address.AddressConvert;
import com.tashow.cloud.member.dal.dataobject.user.MemberUserDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper(uses = {AddressConvert.class})
public interface MemberUserConvert {
MemberUserConvert INSTANCE = Mappers.getMapper(MemberUserConvert.class);
AppMemberUserInfoRespVO convert(MemberUserDO bean);
@Mappings({
@Mapping(source = "bean.id", target = "id"),
})
MemberUserRespDTO convert2(MemberUserDO bean);
List<MemberUserRespDTO> convertList2(List<MemberUserDO> list);
MemberUserDO convert(MemberUserUpdateReqVO bean);
PageResult<MemberUserRespVO> convertPage(PageResult<MemberUserDO> page);
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
MemberUserRespVO convert03(MemberUserDO bean);
}

View File

@@ -1,139 +0,0 @@
package com.tashow.cloud.member.dal.dataobject.user;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.tashow.cloud.common.enums.CommonStatusEnum;
import com.tashow.cloud.common.enums.TerminalEnum;
import com.tashow.cloud.mybatis.mybatis.core.type.LongListTypeHandler;
import com.tashow.cloud.systemapi.enums.common.SexEnum;
import com.tashow.cloud.tenant.core.db.TenantBaseDO;
import lombok.*;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.time.LocalDateTime;
import java.util.List;
/**
* 会员用户 DO
*
* uk_mobile 索引:基于 {@link #mobile} 字段
*
*/
@TableName(value = "member_user", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberUserDO extends TenantBaseDO {
// ========== 账号信息 ==========
/**
* 用户ID
*/
@TableId
private Long id;
/**
* 手机
*/
private String mobile;
/**
* 加密后的密码
*
* 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐
*/
private String password;
/**
* 帐号状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 注册 IP
*/
private String registerIp;
/**
* 注册终端
* 枚举 {@link TerminalEnum}
*/
private Integer registerTerminal;
/**
* 最后登录IP
*/
private String loginIp;
/**
* 最后登录时间
*/
private LocalDateTime loginDate;
// ========== 基础信息 ==========
/**
* 用户昵称
*/
private String nickname;
/**
* 用户头像
*/
private String avatar;
/**
* 真实名字
*/
private String name;
/**
* 性别
*
* 枚举 {@link SexEnum}
*/
private Integer sex;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 所在地
*
* 关联 {@link Area#getId()} 字段
*/
private Integer areaId;
/**
* 用户备注
*/
private String mark;
// ========== 其它信息 ==========
/**
* 积分
*/
private Integer point;
// TODO 疯狂:增加一个 totalPoint个人信息接口要返回
/**
* 会员标签列表,以逗号分隔
*/
@TableField(typeHandler = LongListTypeHandler.class)
private List<Long> tagIds;
/**
* 会员级别编号
*
* 关联 {@link MemberLevelDO#getId()} 字段
*/
private Long levelId;
/**
* 会员经验
*/
private Integer experience;
/**
* 用户分组编号
*
* 关联 {@link MemberGroupDO#getId()} 字段
*/
private Long groupId;
}

View File

@@ -1,22 +0,0 @@
package com.tashow.cloud.member.dal.mysql.address;
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface MemberAddressMapper extends BaseMapperX<MemberAddressDO> {
default MemberAddressDO selectByIdAndUserId(Long id, Long userId) {
return selectOne(MemberAddressDO::getId, id, MemberAddressDO::getUserId, userId);
}
default List<MemberAddressDO> selectListByUserIdAndDefaulted(Long userId, Boolean defaulted) {
return selectList(new LambdaQueryWrapperX<MemberAddressDO>().eq(MemberAddressDO::getUserId, userId)
.eqIfPresent(MemberAddressDO::getDefaultStatus, defaulted));
}
}

View File

@@ -1,96 +0,0 @@
package com.tashow.cloud.member.dal.mysql.user;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserPageReqVO;
import com.tashow.cloud.member.dal.dataobject.user.MemberUserDO;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.stream.Collectors;
/**
* 会员 User Mapper
*
* @author 芋道源码
*/
@Mapper
public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
default MemberUserDO selectByMobile(String mobile) {
return selectOne(MemberUserDO::getMobile, mobile);
}
default List<MemberUserDO> selectListByNicknameLike(String nickname) {
return selectList(new LambdaQueryWrapperX<MemberUserDO>()
.likeIfPresent(MemberUserDO::getNickname, nickname));
}
default PageResult<MemberUserDO> selectPage(MemberUserPageReqVO reqVO) {
// 处理 tagIds 过滤条件
String tagIdSql = "";
if (CollUtil.isNotEmpty(reqVO.getTagIds())) {
tagIdSql = reqVO.getTagIds().stream()
.map(tagId -> "FIND_IN_SET(" + tagId + ", tag_ids)")
.collect(Collectors.joining(" OR "));
}
// 分页查询
return selectPage(reqVO, new LambdaQueryWrapperX<MemberUserDO>()
.likeIfPresent(MemberUserDO::getMobile, reqVO.getMobile())
.betweenIfPresent(MemberUserDO::getLoginDate, reqVO.getLoginDate())
.likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname())
.betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(MemberUserDO::getLevelId, reqVO.getLevelId())
.eqIfPresent(MemberUserDO::getGroupId, reqVO.getGroupId())
.apply(StrUtil.isNotEmpty(tagIdSql), tagIdSql)
.orderByDesc(MemberUserDO::getId));
}
default Long selectCountByGroupId(Long groupId) {
return selectCount(MemberUserDO::getGroupId, groupId);
}
default Long selectCountByLevelId(Long levelId) {
return selectCount(MemberUserDO::getLevelId, levelId);
}
default Long selectCountByTagId(Long tagId) {
return selectCount(new LambdaQueryWrapperX<MemberUserDO>()
.apply("FIND_IN_SET({0}, tag_ids)", tagId));
}
/**
* 更新用户积分(增加)
*
* @param id 用户编号
* @param incrCount 增加积分(正数)
*/
default void updatePointIncr(Long id, Integer incrCount) {
Assert.isTrue(incrCount > 0);
LambdaUpdateWrapper<MemberUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<MemberUserDO>()
.setSql(" point = point + " + incrCount)
.eq(MemberUserDO::getId, id);
update(null, lambdaUpdateWrapper);
}
/**
* 更新用户积分(减少)
*
* @param id 用户编号
* @param incrCount 增加积分(负数)
* @return 更新行数
*/
default int updatePointDecr(Long id, Integer incrCount) {
Assert.isTrue(incrCount < 0);
LambdaUpdateWrapper<MemberUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<MemberUserDO>()
.setSql(" point = point + " + incrCount) // 负数,所以使用 + 号
.eq(MemberUserDO::getId, id);
return update(null, lambdaUpdateWrapper);
}
}

View File

@@ -1,4 +0,0 @@
/**
* 占位
*/
package com.tashow.cloud.member.framework.rpc;

View File

@@ -1,4 +0,0 @@
/**
* 占位
*/
package com.tashow.cloud.member.framework.security.core;

View File

@@ -1,4 +0,0 @@
/**
* 消息队列的消费者
*/
package com.tashow.cloud.member.mq.consumer;

View File

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

View File

@@ -1,4 +0,0 @@
/**
* 消息队列的生产者
*/
package com.tashow.cloud.member.mq.producer;

View File

@@ -1,31 +0,0 @@
package com.tashow.cloud.member.mq.producer.user;
import com.tashow.cloud.memberapi.message.user.MemberUserCreateMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
/**
* 会员用户 Producer
*
* @author owen
*/
@Slf4j
@Component
public class MemberUserProducer {
@Resource
private ApplicationContext applicationContext;
/**
* 发送 {@link MemberUserCreateMessage} 消息
*
* @param userId 用户编号
*/
public void sendUserCreateMessage(Long userId) {
applicationContext.publishEvent(new MemberUserCreateMessage().setUserId(userId));
}
}

View File

@@ -1,8 +0,0 @@
/**
* member 模块,我们放会员业务。
* 例如说:会员中心等等
*
* 1. Controller URL以 /member/ 开头,避免和其它 Module 冲突
* 2. DataObject 表名:以 member_ 开头,方便在数据库中区分
*/
package com.tashow.cloud.member;

View File

@@ -1,67 +0,0 @@
package com.tashow.cloud.member.service.address;
import com.tashow.cloud.member.controller.app.address.vo.AppAddressCreateReqVO;
import com.tashow.cloud.member.controller.app.address.vo.AppAddressUpdateReqVO;
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
import jakarta.validation.Valid;
import java.util.List;
/**
* 用户收件地址 Service 接口
*
* @author 芋道源码
*/
public interface AddressService {
/**
* 创建用户收件地址
*
*
* @param userId 用户编号
* @param createReqVO 创建信息
* @return 编号
*/
Long createAddress(Long userId, @Valid AppAddressCreateReqVO createReqVO);
/**
* 更新用户收件地址
*
* @param userId 用户编号
* @param updateReqVO 更新信息
*/
void updateAddress(Long userId, @Valid AppAddressUpdateReqVO updateReqVO);
/**
* 删除用户收件地址
*
* @param userId 用户编号
* @param id 编号
*/
void deleteAddress(Long userId, Long id);
/**
* 获得用户收件地址
*
* @param id 编号
* @return 用户收件地址
*/
MemberAddressDO getAddress(Long userId, Long id);
/**
* 获得用户收件地址列表
*
* @param userId 用户编号
* @return 用户收件地址列表
*/
List<MemberAddressDO> getAddressList(Long userId);
/**
* 获得用户默认的收件地址
*
* @param userId 用户编号
* @return 用户收件地址
*/
MemberAddressDO getDefaultUserAddress(Long userId);
}

View File

@@ -1,97 +0,0 @@
package com.tashow.cloud.member.service.address;
import cn.hutool.core.collection.CollUtil;
import com.tashow.cloud.member.controller.app.address.vo.AppAddressCreateReqVO;
import com.tashow.cloud.member.controller.app.address.vo.AppAddressUpdateReqVO;
import com.tashow.cloud.member.convert.address.AddressConvert;
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
import com.tashow.cloud.member.dal.mysql.address.MemberAddressMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.util.List;
import static com.tashow.cloud.common.exception.util.ServiceExceptionUtil.exception;
import static com.tashow.cloud.memberapi.enums.ErrorCodeConstants.ADDRESS_NOT_EXISTS;
/**
* 用户收件地址 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class AddressServiceImpl implements AddressService {
@Resource
private MemberAddressMapper memberAddressMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createAddress(Long userId, AppAddressCreateReqVO createReqVO) {
// 如果添加的是默认收件地址,则将原默认地址修改为非默认
if (Boolean.TRUE.equals(createReqVO.getDefaultStatus())) {
List<MemberAddressDO> addresses = memberAddressMapper.selectListByUserIdAndDefaulted(userId, true);
addresses.forEach(address -> memberAddressMapper.updateById(new MemberAddressDO().setId(address.getId()).setDefaultStatus(false)));
}
// 插入
MemberAddressDO address = AddressConvert.INSTANCE.convert(createReqVO);
address.setUserId(userId);
memberAddressMapper.insert(address);
// 返回
return address.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateAddress(Long userId, AppAddressUpdateReqVO updateReqVO) {
// 校验存在,校验是否能够操作
validAddressExists(userId, updateReqVO.getId());
// 如果修改的是默认收件地址,则将原默认地址修改为非默认
if (Boolean.TRUE.equals(updateReqVO.getDefaultStatus())) {
List<MemberAddressDO> addresses = memberAddressMapper.selectListByUserIdAndDefaulted(userId, true);
addresses.stream().filter(u -> !u.getId().equals(updateReqVO.getId())) // 排除自己
.forEach(address -> memberAddressMapper.updateById(new MemberAddressDO().setId(address.getId()).setDefaultStatus(false)));
}
// 更新
MemberAddressDO updateObj = AddressConvert.INSTANCE.convert(updateReqVO);
memberAddressMapper.updateById(updateObj);
}
@Override
public void deleteAddress(Long userId, Long id) {
// 校验存在,校验是否能够操作
validAddressExists(userId, id);
// 删除
memberAddressMapper.deleteById(id);
}
private void validAddressExists(Long userId, Long id) {
MemberAddressDO addressDO = getAddress(userId, id);
if (addressDO == null) {
throw exception(ADDRESS_NOT_EXISTS);
}
}
@Override
public MemberAddressDO getAddress(Long userId, Long id) {
return memberAddressMapper.selectByIdAndUserId(id, userId);
}
@Override
public List<MemberAddressDO> getAddressList(Long userId) {
return memberAddressMapper.selectListByUserIdAndDefaulted(userId, null);
}
@Override
public MemberAddressDO getDefaultUserAddress(Long userId) {
List<MemberAddressDO> addresses = memberAddressMapper.selectListByUserIdAndDefaulted(userId, true);
return CollUtil.getFirst(addresses);
}
}

View File

@@ -1,48 +0,0 @@
spring:
main:
lazy-initialization: true # 开启懒加载,加快速度
banner-mode: off # 单元测试,禁用 Banner
--- #################### 数据库相关配置 ####################
spring:
# 数据源配置项
datasource:
name: ruoyi-vue-pro
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式DATABASE_TO_UPPER 配置表和字段使用小写
driver-class-name: org.h2.Driver
username: sa
password:
druid:
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
initial-size: 1 # 单元测试,配置为 1提升启动速度
sql:
init:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
--- #################### 定时任务相关配置 ####################
--- #################### 配置中心相关配置 ####################
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项(单元测试,禁用 Lock4j
--- #################### 监控相关配置 ####################
--- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置
yudao:
info:
base-package: cn.iocoder.yudao.module

View File

@@ -1,4 +0,0 @@
<configuration>
<!-- 引用 Spring Boot 的 logback 基础配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
</configuration>

View File

@@ -1,5 +0,0 @@
DELETE FROM "member_user";
DELETE FROM "member_address";
DELETE FROM "member_tag";
DELETE FROM "member_level";
DELETE FROM "member_group";

View File

@@ -1,113 +0,0 @@
CREATE TABLE IF NOT EXISTS "member_user"
(
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
"nickname" varchar(30) NOT NULL DEFAULT '' COMMENT '用户昵称',
"name" varchar(30) NULL COMMENT '真实名字',
sex tinyint null comment '性别',
birthday datetime null comment '出生日期',
area_id int null comment '所在地',
mark varchar(255) null comment '用户备注',
point int default 0 null comment '积分',
"avatar" varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
"status" tinyint NOT NULL COMMENT '状态',
"mobile" varchar(11) NOT NULL COMMENT '手机号',
"password" varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
"register_ip" varchar(32) NOT NULL COMMENT '注册 IP',
"login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP',
"login_date" datetime NULL DEFAULT NULL COMMENT '最后登录时间',
"tag_ids" varchar(255) NULL DEFAULT NULL COMMENT '用户标签编号列表,以逗号分隔',
"level_id" bigint NULL DEFAULT NULL COMMENT '等级编号',
"experience" bigint NULL DEFAULT NULL COMMENT '经验',
"group_id" bigint NULL DEFAULT NULL 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 '0' COMMENT '是否删除',
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '会员表';
CREATE TABLE IF NOT EXISTS "member_address" (
"id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"user_id" bigint(20) NOT NULL,
"name" varchar(10) NOT NULL,
"mobile" varchar(20) NOT NULL,
"area_id" bigint(20) NOT NULL,
"detail_address" varchar(250) NOT NULL,
"default_status" bit NOT NULL,
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"creator" varchar(64) DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"updater" varchar(64) DEFAULT '',
PRIMARY KEY ("id")
) COMMENT '用户收件地址';
CREATE TABLE IF NOT EXISTS "member_tag"
(
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar NOT NULL,
"creator" varchar DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL default '0',
PRIMARY KEY ("id")
) COMMENT '会员标签';
CREATE TABLE IF NOT EXISTS "member_level"
(
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar NOT NULL,
"experience" int NOT NULL,
"level" int NOT NULL,
"discount_percent" int NOT NULL,
"icon" varchar NOT NULL,
"background_url" varchar NOT NULL,
"creator" varchar DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
"status" tinyint NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
) COMMENT '会员等级';
CREATE TABLE IF NOT EXISTS "member_group"
(
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar NOT NULL,
"remark" varchar NOT NULL,
"status" tinyint NOT NULL DEFAULT '0',
"creator" varchar DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '用户分组';
CREATE TABLE IF NOT EXISTS "member_brokerage_record"
(
"id" int NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"user_id" bigint NOT NULL,
"biz_id" varchar NOT NULL,
"biz_type" varchar NOT NULL,
"title" varchar NOT NULL,
"price" int NOT NULL,
"total_price" int NOT NULL,
"description" varchar NOT NULL,
"status" varchar NOT NULL,
"frozen_days" int NOT NULL,
"unfreeze_time" varchar,
"creator" varchar DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '佣金记录';

View File

@@ -1,10 +1,8 @@
package com.tashow.cloud.product;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* 应用服务启动类

View File

@@ -0,0 +1,55 @@
package com.tashow.cloud.product.api;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.product.service.ProdService;
import com.tashow.cloud.product.service.SkuService;
import com.tashow.cloud.productapi.api.product.ProdApi;
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 jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import static com.tashow.cloud.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class ProdImpl implements ProdApi {
@Resource
private ProdService prodService;
@Resource
private SkuService skuService;
@Override
public CommonResult<ProdDO> getProdInfo(Long id) {
return CommonResult.success(prodService.getProd(id));
}
@Override
public ProdServiceVO getProdService(Long id) {
return prodService.getProdService(id);
}
@Override
public PageResult<ProdListVO> getProdPage(ProdPageReqVO pageReqVO) {
return prodService.getProdPage(pageReqVO);
}
@Override
public CommonResult<Boolean> reduceStocks(Long skuId, Integer stocksNum) {
skuService.reduceStocks(skuId, stocksNum);
return success(true);
}
@Override
public CommonResult<Boolean> increaseStocks(Long skuId, Integer stocksNum) {
skuService.increaseStocks(skuId, stocksNum);
return success(true);
}
}

View File

@@ -3,12 +3,10 @@ package com.tashow.cloud.product.controller.admin;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.product.mapper.ProdMapper;
import com.tashow.cloud.productapi.api.product.dto.ProdDO;
import com.tashow.cloud.product.service.ProdService;
import com.tashow.cloud.productapi.api.product.dto.SkuDO;
import com.tashow.cloud.product.service.SkuService;
import com.tashow.cloud.productapi.api.product.dto.ProdDO;
import com.tashow.cloud.productapi.api.product.vo.prod.*;
import com.tashow.cloud.productapi.api.product.vo.sku.SkuPageReqVO;
import com.tashow.cloud.productapi.api.product.vo.sku.SkuRecycleBinVO;
import com.tashow.cloud.productapi.enums.BaseEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -33,6 +31,8 @@ public class ProdController {
@Resource
private ProdService prodService;
@Resource
private SkuService skuService;
@Resource
private ProdMapper prodMapper;
@PostMapping("/create")
@Operation(summary = "创建商品")
@@ -121,15 +121,14 @@ public class ProdController {
/*
@GetMapping("/get")
@Operation(summary = "获得商品")
@GetMapping("/getProdInfo")
@Operation(summary = "获得商品详情a")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('tashow-module-product:prod:query')")
public CommonResult<ProdRespVO> getProd(@RequestParam("id") Long id) {
@PermitAll
public CommonResult<ProdDO> getProdInfo(@RequestParam("id") Long id) {
ProdDO prod = prodService.getProd(id);
return success(BeanUtils.toBean(prod, ProdRespVO.class));
}*/
return success(prod);
}
@PermitAll
@GetMapping("/page")
@@ -158,4 +157,23 @@ public class ProdController {
}
@PostMapping("/reduceStocks")
@Operation(summary = "扣减库存")
@PermitAll
public CommonResult<Boolean> reduceStocks(@RequestParam("skuid") Long skuId,
@RequestParam("stocksNum") Integer stocksNum) {
skuService.reduceStocks(skuId, stocksNum);
return success(true);
}
@PostMapping("/increaseStocks")
@Operation(summary = "增加库存")
@PermitAll
public CommonResult<Boolean> increaseStocks(@RequestParam("skuid") Long skuId,
@RequestParam("stocksNum") Integer stocksNum) {
skuService.increaseStocks(skuId, stocksNum);
return success(true);
}
}

View File

@@ -9,6 +9,7 @@ import com.tashow.cloud.productapi.api.product.dto.SkuDO;
import com.tashow.cloud.productapi.api.product.vo.sku.SkuRecycleBinVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
/**
* 单品SKU Mapper
@@ -44,4 +45,25 @@ public interface SkuMapper extends BaseMapperX<SkuDO> {
// 查询商品下所有 SKU 的 is_shelf 状态(未删除)
List<Integer> selectShelfStatusByProdId(@Param("prodId") Long prodId);
/**
* 扣减库存(带乐观锁)
* @param skuId 单品ID
* @param stocksNum 扣减数量
* @param version 版本号
* @return 更新影响的行数
*/
@Update("UPDATE tz_sku SET stocks = stocks - #{stocksNum}, version = version + 1, update_time = NOW() " +
"WHERE sku_id = #{skuId} AND version = #{version} AND stocks >= #{stocksNum} AND deleted = 0")
int reduceStocks(@Param("skuId") Long skuId,
@Param("stocksNum") Integer stocksNum,
@Param("version") Integer version);
/**
* 增加库存
*/
@Update("UPDATE tz_sku SET stocks = stocks + #{stocksNum}, version = version + 1, update_time = NOW() " +
"WHERE sku_id = #{skuId} AND deleted = 0")
int increaseStocks(@Param("skuId") Long skuId,
@Param("stocksNum") Integer stocksNum);
}

View File

@@ -0,0 +1,43 @@
package com.tashow.cloud.product.security.config;
import com.tashow.cloud.productapi.enums.ApiConstants;
import com.tashow.cloud.security.security.config.AuthorizeRequestsCustomizer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
/**
* Infra 模块的 Security 配置
*/
@Configuration(proxyBeanMethods = false, value = "prodSecurityConfiguration")
public class ProdSecurityConfiguration {
@Value("${spring.boot.admin.context-path:''}")
private String adminSeverContextPath;
@Bean("prodAuthorizeRequestsCustomizer")
public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
return new AuthorizeRequestsCustomizer() {
@Override
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
// Spring Boot Actuator 的安全配置
registry.requestMatchers("/actuator").permitAll()
.requestMatchers("/actuator/**").permitAll();
// Druid 监控
registry.requestMatchers("/druid/**").permitAll();
// Spring Boot Admin Server 的安全配置
registry.requestMatchers(adminSeverContextPath).permitAll()
.requestMatchers(adminSeverContextPath + "/**").permitAll();
// TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案
// RPC 服务的安全配置
registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
}
};
}
}

View File

@@ -175,5 +175,17 @@ public interface SkuService {
* @param skuServiceDeliverList 更新信息
*/
void updateDeliver(List<SkuServiceDeliverDO> skuServiceDeliverList);
/**
* 扣减库存
* @param skuId 单品ID
* @param stocksNum 扣减数量
*/
void reduceStocks(Long skuId, Integer stocksNum);
/**
* 增加库存
* @param skuId 单品ID
* @param stocksNum 增加数量
*/
void increaseStocks(Long skuId, Integer stocksNum);
}

View File

@@ -18,6 +18,7 @@ import com.tashow.cloud.productapi.api.product.vo.sku.*;
import com.tashow.cloud.productapi.enums.BaseEnum;
import com.tashow.cloud.productapi.enums.ErrorCodeConstants;
import com.tashow.cloud.productapi.enums.ServiceTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
@@ -41,6 +42,7 @@ import static com.tashow.cloud.common.exception.util.ServiceExceptionUtil.except
*/
@Service
@Validated
@Slf4j
public class SkuServiceImpl implements SkuService {
@Resource
@@ -967,6 +969,62 @@ public class SkuServiceImpl implements SkuService {
skuServiceDeliverMapper.insertBatch(skuServiceDeliverList);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void reduceStocks(Long skuId, Integer stocksNum) {
if (stocksNum == null || stocksNum <= 0) {
stocksNum=1;
}
// 查询SKU信息
SkuDO sku = skuMapper.selectById(skuId);
if (sku == null) {
//商品不存在
throw exception(ErrorCodeConstants.SKU_DOES_NOT_EXIST);
}
if (sku.getDeleted() == 1) {
//商品已删除
throw exception(ErrorCodeConstants.SKU_DELETE);
}
if (sku.getStatus() == 0) {
//商品已禁用
throw exception(ErrorCodeConstants.SKU_DISABLE);
}
// 检查无限库存标志
if (sku.getStocksFlg() != null && sku.getStocksFlg() == 1) {
log.info("SKU {} 为无限库存,无需扣减", skuId);
return;
}
// 检查库存是否充足
if (sku.getStocks() < stocksNum) {
throw new IllegalArgumentException("库存不足,当前库存:" + sku.getStocks() + ",需要:" + stocksNum);
}
// 扣减库存
int updateRows = skuMapper.reduceStocks(skuId, stocksNum, sku.getVersion());
if (updateRows == 0) {
// 可能是版本号冲突或库存不足
SkuDO currentSku = skuMapper.selectById(skuId);
if (currentSku.getStocks() < stocksNum) {
throw new IllegalArgumentException("库存不足,当前库存:" + sku.getStocks() + ",需要:" + stocksNum);
} else {
throw new IllegalArgumentException("扣减库存失败,请重试");
}
}
log.info("扣减库存成功 - SKU: {}, 扣减数量: {}, 剩余库存: {}",skuId, stocksNum, sku.getStocks() - stocksNum);
}
@Override
public void increaseStocks(Long skuId, Integer stocksNum) {
// 查询SKU信息
SkuDO sku = skuMapper.selectById(skuId);
// 检查无限库存标志
if (sku.getStocksFlg() != null && sku.getStocksFlg() == 1) {
log.info("SKU {} 为无限库存,无需增加", skuId);
return;
}
skuMapper.increaseStocks(skuId, stocksNum);
}
}

View File

@@ -7,10 +7,10 @@ spring:
username: nacos # Nacos 账号
password: nacos # Nacos 密码
discovery: # 【配置中心】配置项
namespace: 16bd40df-7cc7-4c2c-82c2-6186ade7bb08 # 命名空间。这里使用 dev 开发环境
namespace: dev # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
metadata:
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
config: # 【注册中心】配置项
namespace: 16bd40df-7cc7-4c2c-82c2-6186ade7bb08 # 命名空间。这里使用 dev 开发环境
namespace: dev # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP

View File

@@ -551,6 +551,7 @@
<result property="deleteTime" column="delete_time"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="pic" column="pic"/>
<collection property="areaNameList" ofType="string">
<result column="area_name"/>
</collection>
@@ -585,7 +586,7 @@
<select id="getProdPageList" resultMap="ProdListMap" >
select tp.prod_id,tp.prod_name,tp.category_name,tp.shop_id,tsd.shop_name,tpsa.area_name,min_eri.response_hours,
tprc.reservation_time_slots,tp.delete_time,tp.create_time,tp.update_time,tp.status,tp.is_prohibit,
tprc.reservation_time_slots,tp.delete_time,tp.create_time,tp.update_time,tp.status,tp.is_prohibit,tp.pic,
tp.process_notes processNotes
from tz_prod tp
left join tz_shop_detail tsd on tp.shop_id = tsd.shop_id
@@ -604,7 +605,7 @@
<if test="createTime != null and createTime.length == 2">
AND tp.create_time BETWEEN #{createTime[0]} AND #{createTime[1]}
</if>
<if test="prodName != null">
<if test="prodName != null and prodName != ''">
AND tp.prod_name = #{prodName}
</if>
<if test="shopId != null">

View File

@@ -1,9 +1,5 @@
package com.tashow.cloud.system.controller.admin.auth;
import static com.tashow.cloud.common.pojo.CommonResult.success;
import static com.tashow.cloud.common.util.collection.CollectionUtils.convertSet;
import static com.tashow.cloud.web.web.core.util.WebFrameworkUtils.getLoginUserId;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.tashow.cloud.common.enums.CommonStatusEnum;
@@ -11,14 +7,7 @@ import com.tashow.cloud.common.enums.UserTypeEnum;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.security.security.config.SecurityProperties;
import com.tashow.cloud.security.security.core.util.SecurityFrameworkUtils;
import com.tashow.cloud.system.controller.admin.auth.vo.AuthLoginReqVO;
import com.tashow.cloud.system.controller.admin.auth.vo.AuthLoginRespVO;
import com.tashow.cloud.system.controller.admin.auth.vo.AuthPermissionInfoRespVO;
import com.tashow.cloud.system.controller.admin.auth.vo.AuthRegisterReqVO;
import com.tashow.cloud.system.controller.admin.auth.vo.AuthResetPasswordReqVO;
import com.tashow.cloud.system.controller.admin.auth.vo.AuthSmsLoginReqVO;
import com.tashow.cloud.system.controller.admin.auth.vo.AuthSmsSendReqVO;
import com.tashow.cloud.system.controller.admin.auth.vo.AuthSocialLoginReqVO;
import com.tashow.cloud.system.controller.admin.auth.vo.*;
import com.tashow.cloud.system.convert.auth.AuthConvert;
import com.tashow.cloud.system.dal.dataobject.permission.MenuDO;
import com.tashow.cloud.system.dal.dataobject.permission.RoleDO;
@@ -34,17 +23,17 @@ import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import static com.tashow.cloud.common.pojo.CommonResult.success;
import static com.tashow.cloud.common.util.collection.CollectionUtils.convertSet;
import static com.tashow.cloud.web.web.core.util.WebFrameworkUtils.getLoginUserId;
/** 管理后台 - 认证 */
@RestController

View File

@@ -70,6 +70,7 @@ public class OAuth2AccessTokenDO extends TenantBaseDO {
/**
* 过期时间
*/
private LocalDateTime expiresTime;
}

View File

@@ -46,6 +46,10 @@
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-trade-api</artifactId>
</dependency>
<dependency>
<groupId>com.tashow.cloud</groupId>
<artifactId>tashow-product-api</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>

View File

@@ -4,13 +4,14 @@ import cn.hutool.core.collection.CollUtil;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.memberapi.api.user.MemberUserApi;
import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderPageRespVO;
import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderPageReqVO;
import com.tashow.cloud.productapi.api.product.ProdApi;
import com.tashow.cloud.productapi.api.product.dto.ProdDO;
import com.tashow.cloud.trade.controller.admin.order.vo.*;
import com.tashow.cloud.trade.convert.order.TradeOrderConvert;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderLogDO;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderSubLogDO;
import com.tashow.cloud.trade.service.order.TradeOrderLogService;
import com.tashow.cloud.trade.service.order.TradeOrderQueryService;
import com.tashow.cloud.trade.service.order.TradeOrderUpdateService;
@@ -19,10 +20,7 @@ import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -48,9 +46,12 @@ public class TradeOrderController {
@Resource
private MemberUserApi memberUserApi;
@Resource
private ProdApi prodApi;
/**
* 获得交易订单分页
*
* @param reqVO
* @return
*/
@@ -72,6 +73,7 @@ public class TradeOrderController {
/**
* 获得交易订单详情
*
* @param id 订单编号
* @return
*/
@@ -87,87 +89,96 @@ public class TradeOrderController {
// 查询订单项
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(id);
// 拼接数据
// MemberUserRespDTO user = memberUserApi.getUser(order.getUserId()).getCheckedData();
// MemberUserRespDTO brokerageUser = order.getBrokerageUserId() != null ?
// memberUserApi.getUser(order.getBrokerageUserId()).getCheckedData() : null;
List<TradeOrderLogDO> orderLogs = tradeOrderLogService.getOrderLogListByOrderId(id);
return success(TradeOrderConvert.INSTANCE.convert(order, orderItems, orderLogs, null, null));
return success(TradeOrderConvert.INSTANCE.convert(order, orderItems, orderLogs));
}
// // 获得交易订单统计
// @GetMapping("/summary")
/**
* 获得交易订单统计
*
* @return
*/
@GetMapping("/summary")
@PermitAll
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
// public CommonResult<TradeOrderSummaryRespVO> getOrderSummary(TradeOrderPageReqVO reqVO) {
// return success(tradeOrderQueryService.getOrderSummary(reqVO));
// }
//
// // 获得交易订单的物流轨迹
// // id: 交易订单编号
// @GetMapping("/get-express-track-list")
public CommonResult<TradeOrderSummaryRespVO> getOrderSummary() {
return success(tradeOrderQueryService.getOrderSummary());
}
/**
* 取消订单
*
* @return
*/
@PutMapping("/cancel")
@PermitAll
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
// public CommonResult<List<?>> getOrderExpressTrackList(@RequestParam("id") Long id) {
// return success(TradeOrderConvert.INSTANCE.convertList02(
// tradeOrderQueryService.getExpressTrackList(id)));
// }
//
// // 订单发货
// @PutMapping("/delivery")
// @PreAuthorize("@ss.hasPermission('trade:order:update')")
// public CommonResult<Boolean> deliveryOrder(@RequestBody TradeOrderDeliveryReqVO deliveryReqVO) {
// tradeOrderUpdateService.deliveryOrder(deliveryReqVO);
// return success(true);
// }
//
// // 订单备注
// @PutMapping("/update-remark")
// @PreAuthorize("@ss.hasPermission('trade:order:update')")
// public CommonResult<Boolean> updateOrderRemark(@RequestBody TradeOrderRemarkReqVO reqVO) {
// tradeOrderUpdateService.updateOrderRemark(reqVO);
// return success(true);
// }
//
// // 订单调价
// @PutMapping("/update-price")
// @PreAuthorize("@ss.hasPermission('trade:order:update')")
// public CommonResult<Boolean> updateOrderPrice(@RequestBody TradeOrderUpdatePriceReqVO reqVO) {
// tradeOrderUpdateService.updateOrderPrice(reqVO);
// return success(true);
// }
//
// // 修改订单收货地址
// @PutMapping("/update-address")
// @PreAuthorize("@ss.hasPermission('trade:order:update')")
// public CommonResult<Boolean> updateOrderAddress(@RequestBody TradeOrderUpdateAddressReqVO reqVO) {
// tradeOrderUpdateService.updateOrderAddress(reqVO);
// return success(true);
// }
//
// // 订单核销
// // id: 交易订单编号
// @PutMapping("/pick-up-by-id")
// @PreAuthorize("@ss.hasPermission('trade:order:pick-up')")
// public CommonResult<Boolean> pickUpOrderById(@RequestParam("id") Long id) {
// tradeOrderUpdateService.pickUpOrderByAdmin(getLoginUserId(), id);
// return success(true);
// }
//
// // 订单核销
// // pickUpVerifyCode: 自提核销码
// @PutMapping("/pick-up-by-verify-code")
// @PreAuthorize("@ss.hasPermission('trade:order:pick-up')")
// public CommonResult<Boolean> pickUpOrderByVerifyCode(@RequestParam("pickUpVerifyCode") String pickUpVerifyCode) {
// tradeOrderUpdateService.pickUpOrderByAdmin(getLoginUserId(), pickUpVerifyCode);
// return success(true);
// }
//
// // 查询核销码对应的订单
// // pickUpVerifyCode: 自提核销码
// @GetMapping("/get-by-pick-up-verify-code")
public CommonResult<Boolean> cancel(@RequestParam("id") Long id) {
return success(tradeOrderUpdateService.cancelOrderByAdmin(id));
}
/**
* 接单确认
*
* @return
*/
@PutMapping("/acceptConfirm")
@PermitAll
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
// public CommonResult<TradeOrderDetailRespVO> getByPickUpVerifyCode(@RequestParam("pickUpVerifyCode") String pickUpVerifyCode) {
// TradeOrderDO tradeOrder = tradeOrderUpdateService.getByPickUpVerifyCode(pickUpVerifyCode);
// return success(TradeOrderConvert.INSTANCE.convert2(tradeOrder, null));
// }
public CommonResult<Boolean> acceptConfirm(@RequestParam("id") Long id) {
tradeOrderUpdateService.acceptConfirmOrderByAdmin(id);
return success(true);
}
/**
* 服务上报
*
* @return
*/
@PutMapping("/reportServe")
@PermitAll
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
public CommonResult<Boolean> reportServe(@RequestParam("id") Long id) {
tradeOrderUpdateService.reportServeOrderByAdmin(id);
return success(true);
}
// 订单备注
@PutMapping("/update-remark")
@PermitAll
// @PreAuthorize("@ss.hasPermission('trade:order:update')")
public CommonResult<Boolean> updateOrderRemark(@RequestBody TradeOrderRemarkReqVO reqVO) {
tradeOrderUpdateService.updateOrderRemark(reqVO);
return success(true);
}
/**
* 交易快照
* @return
*/
@GetMapping("/fastPhoto")
@PermitAll
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
public CommonResult<TradeOrderFastPhotoRespVo> fastPhoto(@Valid TradeOrderFastPhotoReqVo reqVo) {
//获取交易订单详情
TradeOrderItemDO orderItem = tradeOrderQueryService.getOrderItem(reqVo.getItemId());
//获取产品详情
CommonResult<ProdDO> prodInfo = prodApi.getProdInfo(reqVo.getSpuId());
return success(TradeOrderConvert.INSTANCE.convert(orderItem,prodInfo.getData()));
}
/**
* 服务信息修改记录
*
* @return
*/
@GetMapping("/subTimeLog/{id}")
@PermitAll
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
public CommonResult<List<TradeOrderSubLogDO>> subTimeLog(@PathVariable("id") Long id) {
//获取订单预约时间修改列表
List<TradeOrderSubLogDO> subLogDO = tradeOrderQueryService.getSubTimeLogList(id);
return success(subLogDO);
}
}

View File

@@ -2,6 +2,12 @@ package com.tashow.cloud.trade.controller.admin.order.vo;
// 移除Swagger相关导入
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.tashow.cloud.common.enums.TerminalEnum;
import com.tashow.cloud.common.util.json.databind.StringLocalDateTimeSerializer;
import com.tashow.cloud.common.validation.InEnum;
import com.tashow.cloud.tradeapi.enums.order.TradeOrderStatusEnum;
import com.tashow.cloud.tradeapi.enums.order.TradeOrderTypeEnum;
import lombok.Data;
import java.time.LocalDateTime;
@@ -15,17 +21,27 @@ public class TradeOrderBaseVO {
// 订单编号
private Long id;
// 订单流水
// 订单
private String orderNum;
// 下单时间
private LocalDateTime createTime;
/**
* 订单类型
* {@link TradeOrderTypeEnum#getType()}
*/
private Integer orderType;
// 订单类目
private String orderCategoryName;
// 订单来源
/**
* 订单来源
* {@link TerminalEnum#getTerminal()}
*/
@InEnum(TerminalEnum.class)
private Integer orderTerminal;
/**
* 订单状态
* {@link TradeOrderStatusEnum#getStatus()}
*/
@InEnum(TradeOrderStatusEnum.class)
private Integer orderStatus;
// 用户编号
private Long userId;
@@ -37,35 +53,14 @@ public class TradeOrderBaseVO {
private String userAvatar;
//用户手机号
private String userMobile;
// 订单状态
private Integer orderStatus;
//商品图片
private String picUrl;
//商品名称
private String spuName;
//商品规格
private String skuName;
// 购买的商品数量
private Integer count;
// 单价
private Integer price;
//到手价
private Integer handedPrice;
//实付金额
private Integer payPrice;
// 单位
private String unit;
//预约时间
private String subTime;
//服务地址
private String serveAddress;
// 用户备注 - 必填,示例:你猜
private String userRemark;
//支付方式
private String payType;
//财务状态
private String financeStatus;
// 下单时间
@JsonSerialize(using = StringLocalDateTimeSerializer.class)
private LocalDateTime createTime;
// 订单完成时间
@JsonSerialize(using = StringLocalDateTimeSerializer.class)
private LocalDateTime finishTime;
}

View File

@@ -2,6 +2,10 @@ package com.tashow.cloud.trade.controller.admin.order.vo;
// 移除Swagger相关导入
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.tashow.cloud.common.util.json.databind.StringLocalDateTimeSerializer;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -11,151 +15,112 @@ import java.util.List;
// 管理后台 - 交易订单的详情 Response VO
@Data
@Accessors(chain = true)
public class TradeOrderDetailRespVO extends TradeOrderBaseVO {
public class TradeOrderDetailRespVO extends TradeOrderBaseVO{
//基本信息
private TradeOrderBaseInfo tradeOrderInfoBase;
//商品信息
private TradeProductInfo tradeProductInfo;
//扩展服务信息
private String tradeExtendServeInfo;
//附加费信息
private String tradeExtendCostInfo;
//配送信息
private TradeDeliveryInfo tradeDeliveryInfo;
//订单类目id
private Long orderCategoryId;
//订单类目名称
private String orderCategoryName;
//取消原因
private String cancelReason;
//商家备注
private String merchantRemark;
//退款时间
private LocalDateTime refundTime;
//保障时间
private LocalDateTime propertyTime;
//保障状态
private Integer propertyStatus;
//订单金额
private Integer price;
//实付金额
private Integer payPrice;
//优惠金额
private Integer discountPrice;
//退款金额
private Integer refundPrice;
//实收金额
private Integer livePrice;
//支付方式
private Integer payType;
//支付渠道 (线上线下)
private Integer payChannelCode;
//倒计时()
private String payLastTime;
//财务状态
private Integer financeStatus;
//交易流水号
private String payOrderId;
//支付时间
@JsonSerialize(using = StringLocalDateTimeSerializer.class)
private LocalDateTime payTime;
//取消时间
@JsonSerialize(using = StringLocalDateTimeSerializer.class)
private LocalDateTime cancelTime;
//订单状态流转记录
List<TradeOrderStatusRespVo> statusList;
//商品列表
private List<Item> items;
//服务信息(order_serve_info配置)
private JSONObject tradeServeInfo;
//扩展服务信息(order_serve_info配置)
private JSONArray tradeExtendServeInfo;
//附加费信息(order_serve_info配置)
private JSONArray tradeExtendCostInfo;
@Data
@Accessors(chain = true)
class TradeProductInfo{
public static class Item {
//店铺log
private String shopLogoUrl;
//店铺名称
private Long id;
//店铺名称
private String shopName;
//店铺logo
private String shopLogo;
//商品id
private Long spuId;
//skuid
//skuId
private Long skuId;
//商品图片
private String picUrl;
//商品规格图
private String skuPicUrl;
//商品名称
private String spuName;
//商品规格
private String skuName;
//购买数量
private Integer count;
//商品单价
private Integer price;
//商品单位
private String unit;
//到手价(单价 - 优惠)
private Integer handedPrice;
//成本价
private Integer expensePrice;
//保障
private List<String> properties;
//服务内容
private String serveContent;
//订单类目id
private Long orderCategoryId;
//订单类目名称
private String orderCategoryName;
// 购买的商品数量
private Integer count;
// 订单总价
private Integer price;
// 单位
private String unit;
//商品总价
private Integer totalPrice;
//优惠金额
private Integer discountPrice;
//实付金额
private Integer payPrice;
//累计退款金额
private Integer refundPrice;
//累计退款数量
private Integer refundCount;
//到手价
private Integer handedPrice;
//成本价
private Integer expensePrice;
//服务保障列表
private String properties;
//服务内容
private String serveContent;
}
@Data
@Accessors(chain = true)
class TradeOrderBaseInfo{
//订单id
private String id;
//订单编号
private String orderNo;
//订单状态
private String orderStatus;
//订单类型
private String orderType;
//订单类目
private String orderCategoryNameAndId;
//订单来源
private String orderTerminal;
//用户信息
private String userInfo;
//取消时间
private LocalDateTime cancelTime;
//取消原因
private String cancelReason;
//商家备注
private String merchantRemark;
//退款时间
private String refundTime;
//保障时间
private String propertyTime;
//保障状态
private String propertyStatus;
//订单总价
private String price;
//优惠金额
private String discountPrice;
//实付金额
private Integer payPrice;
//退款金额
private String refundPrice;
//实收金额
private String livePrice;
//支付方式(支付宝微信)
private String payType;
//支付渠道 (线上线下)
private String payChannelCode;
//交易流水号
private String payOrderId;
//支付时间
private LocalDateTime payTime;
//创建时间
private LocalDateTime createTime;
//完成时间
private LocalDateTime finishTime;
}
@Data
@Accessors(chain = true)
class TradeDeliveryInfo{
//承运方
private String logisticsName;
//送货方式
private String logisticsType;
//送货上门
private String logisticsNum;
//收货人
private String receiverName;
//收货手机
private String receiverMobile;
//收货地址
private String receiverDetailAddress;
//快递详情
private String deliveryDetail;
}
}

View File

@@ -0,0 +1,15 @@
package com.tashow.cloud.trade.controller.admin.order.vo;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class TradeOrderFastPhotoReqVo {
//订单项id
@NotNull(message = "订单详情编号不能为空")
private Long itemId;
//产品id
@NotNull(message = "产品编号不能为空")
private Long spuId;
}

View File

@@ -0,0 +1,20 @@
package com.tashow.cloud.trade.controller.admin.order.vo;
import lombok.Data;
@Data
public class TradeOrderFastPhotoRespVo {
//产品主图
private String pic;
//产品轮播图
private String imgs;
//商品名称
private String spuName;
//商品概述
private String brief;
//sku
private String skuName;
//商品详细描述
private String content;
}

View File

@@ -1,6 +1,7 @@
package com.tashow.cloud.trade.controller.admin.order.vo;
// 移除Swagger相关导入
import lombok.Data;
/**
@@ -8,60 +9,7 @@ import lombok.Data;
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class TradeOrderItemBaseVO {
public class TradeOrderItemBaseVO extends TradeOrderBaseVO{
// ========== 订单项基本信息 ==========
// 编号 - 必填示例1
private Long id;
// 用户编号 - 必填示例1
private Long userId;
// 订单编号 - 必填示例1
private Long orderId;
// ========== 商品基本信息 ==========
// 商品 SPU 编号 - 必填示例1
private Long spuId;
// 商品 SPU 名称 - 必填,示例:芋道源码
private String spuName;
// 商品 SKU 编号 - 必填示例1
private Long skuId;
// 商品图片 - 必填示例https://www.iocoder.cn/1.png
private String picUrl;
// 购买数量 - 必填示例1
private Integer count;
// ========== 价格 + 支付基本信息 ==========
// 商品原价(单) - 必填示例100
private Integer price;
// 商品优惠(总) - 必填示例100
private Integer discountPrice;
// 商品实付金额(总) - 必填示例100
private Integer payPrice;
// 子订单分摊金额(总) - 必填示例100
private Integer orderPartPrice;
// 分摊后子订单实付金额(总) - 必填示例100
private Integer orderDividePrice;
// ========== 营销基本信息 ==========
// TODO 芋艿:在捉摸一下
// ========== 售后基本信息 ==========
// 售后状态 - 必填示例1
private Integer afterSaleStatus;
}

View File

@@ -1,11 +1,52 @@
package com.tashow.cloud.trade.controller.admin.order.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.tashow.cloud.common.util.json.databind.StringLocalDateTimeSerializer;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
// 管理后台 - 交易订单的分页项 Response VO
@Data
public class TradeOrderPageRespVO extends TradeOrderBaseVO {
//订单项列表
private List<Item> items;
//店铺名称
private String shopName;
//店铺log
private String shopLogo;
//支付剩余时间
private String payLastTime;
@Data
public static class Item {
//商品图片
private String picUrl;
//商品名称
private String spuName;
//商品规格
private String skuName;
// 购买的商品数量
private Integer count;
// 订单总价
private Integer price;
// 订单总价
private Integer discountPrice;
//到手价
private Integer handedPrice;
//实付金额
private Integer payPrice;
// 单位
private String unit;
//预约时间
@JsonSerialize(using = StringLocalDateTimeSerializer.class)
private LocalDateTime subTime;
//服务地址
private String serveAddress;
}
}

View File

@@ -0,0 +1,43 @@
package com.tashow.cloud.trade.controller.admin.order.vo;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO;
import com.tashow.cloud.tradeapi.enums.order.TradeOrderOperateTypeEnum;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class TradeOrderStatusRespVo {
private Long id;
/**
* 订单号
*
* 关联 {@link TradeOrderDO#getId()}
*/
private Long orderId;
/**
* 操作前状态
*/
private Integer beforeStatus;
/**
* 操作后状态
*/
private Integer afterStatus;
/**
* 操作类型
*
* {@link TradeOrderOperateTypeEnum}
*/
private Integer operateType;
/**
* 订单日志信息
*/
private String content;
/**
* 订单日志信息
*/
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,12 @@
package com.tashow.cloud.trade.controller.admin.order.vo;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderSubLogDO;
import lombok.Data;
import java.util.List;
@Data
public class TradeOrderSubTimeListRespVo {
private List<TradeOrderSubLogDO> subTimeList;
}

View File

@@ -10,13 +10,10 @@ public class TradeOrderSummaryRespVO {
// 订单数量 - 必填示例1024
private Long orderCount;
// 订单金额 - 必填示例1024
private Long orderPayPrice;
// 实付金额 - 必填示例1024
private Long payPrice;
// 退款单数 - 必填示例1024
private Long afterSaleCount;
// 退款金额 - 必填示例1024
private Long afterSalePrice;
// 最终实收金额 - 必填示例1024
private Long livePrice;
}

View File

@@ -137,12 +137,12 @@ public class AppTradeOrderController {
Map<String, Long> orderCount = Maps.newLinkedHashMapWithExpectedSize(5);
// 全部
orderCount.put("allCount", tradeOrderQueryService.getOrderCount(getLoginUserId(), null, null));
// 待付款(未支付)
orderCount.put("unpaidCount", tradeOrderQueryService.getOrderCount(getLoginUserId(),
TradeOrderStatusEnum.UNPAID.getStatus(), null));
// 待发货
orderCount.put("undeliveredCount", tradeOrderQueryService.getOrderCount(getLoginUserId(),
TradeOrderStatusEnum.UNDELIVERED.getStatus(), null));
// // 待付款(未支付)
// orderCount.put("unpaidCount", tradeOrderQueryService.getOrderCount(getLoginUserId(),
// TradeOrderStatusEnum.UNPAID.getStatus(), null));
// // 待发货
// orderCount.put("undeliveredCount", tradeOrderQueryService.getOrderCount(getLoginUserId(),
// TradeOrderStatusEnum.UNDELIVERED.getStatus(), null));
// 待收货
orderCount.put("deliveredCount", tradeOrderQueryService.getOrderCount(getLoginUserId(),
TradeOrderStatusEnum.DELIVERED.getStatus(), null));

View File

@@ -30,6 +30,7 @@ public interface AfterSaleConvert {
@Mappings({
@Mapping(target = "id", ignore = true),
@Mapping(target = "refundPrice", ignore = true),
@Mapping(target = "createTime", ignore = true),
@Mapping(target = "updateTime", ignore = true),
@Mapping(target = "creator", ignore = true),

View File

@@ -2,8 +2,10 @@ package com.tashow.cloud.trade.convert.order;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.common.util.collection.CollectionUtils;
import com.tashow.cloud.common.util.date.LocalDateTimeUtils;
import com.tashow.cloud.common.util.ip.AreaUtils;
import com.tashow.cloud.common.util.string.StrUtils;
import com.tashow.cloud.excel.dict.core.DictFrameworkUtils;
@@ -11,6 +13,8 @@ import com.tashow.cloud.memberapi.api.address.dto.MemberAddressRespDTO;
import com.tashow.cloud.memberapi.api.user.dto.MemberUserRespDTO;
import com.tashow.cloud.payapi.api.order.dto.PayOrderCreateReqDTO;
import com.tashow.cloud.payapi.enums.DictTypeConstants;
import com.tashow.cloud.productapi.api.product.dto.ProdDO;
import com.tashow.cloud.productapi.api.product.vo.prod.ProdRespVO;
import com.tashow.cloud.trade.controller.admin.base.member.user.MemberUserRespVO;
import com.tashow.cloud.trade.controller.admin.order.vo.*;
import com.tashow.cloud.trade.controller.app.order.vo.*;
@@ -29,15 +33,17 @@ import com.tashow.cloud.trade.service.brokerage.bo.BrokerageAddReqBO;
import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO;
import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO;
import com.tashow.cloud.tradeapi.api.order.dto.TradeOrderRespDTO;
import com.tashow.cloud.tradeapi.enums.order.TradeOrderStatusEnum;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static com.tashow.cloud.common.util.collection.CollectionUtils.convertMap;
import static com.tashow.cloud.common.util.collection.CollectionUtils.convertMultiMap;
@@ -50,15 +56,16 @@ public interface TradeOrderConvert {
TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
//region 分页结果
default PageResult<TradeOrderPageRespVO> convertPage(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems) {
Map<Long, TradeOrderItemDO> orderItemMap = convertMap(orderItems, TradeOrderItemDO::getOrderId);
Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
// 转化 List
List<TradeOrderPageRespVO> orderVOs = CollectionUtils.convertList(pageResult.getList(), order -> {
TradeOrderItemDO itemDO = orderItemMap.get(order.getId());
TradeOrderPageRespVO orderVO = convert(order, itemDO);
orderVO.setHandedPrice(itemDO.getPrice()-itemDO.getDiscountPrice());
return orderVO;
List<TradeOrderItemDO> itemDO = orderItemMap.get(order.getId());
TradeOrderPageRespVO orderPageRespVO = convert(order, itemDO);
orderPageRespVO.getItems().stream().forEach(item -> {
item.setHandedPrice(item.getPrice() - item.getDiscountPrice());
});
return orderPageRespVO;
});
return new PageResult<>(orderVOs, pageResult.getTotal());
}
@@ -66,13 +73,54 @@ public interface TradeOrderConvert {
@Mappings({
@Mapping(source = "order.id", target = "id"),
@Mapping(source = "order.userId", target = "userId"),
@Mapping(source = "items.createTime", target = "createTime"),
@Mapping(source = "items.price", target = "price"),
@Mapping(source = "items.payPrice", target = "payPrice"),
})
TradeOrderPageRespVO convert(TradeOrderDO order, TradeOrderItemDO items);
TradeOrderPageRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> items);
//endregion
//region 订单详情
default TradeOrderDetailRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems, List<TradeOrderLogDO> orderLogs) {
//订单基本信息
TradeOrderDetailRespVO orderVO = convert2(order, orderItems, orderLogs);
//计算商品到手价
orderVO.getItems().stream().forEach(item -> {
item.setHandedPrice(item.getPrice() - item.getDiscountPrice());
item.setTotalPrice(item.getPrice() * item.getCount() - item.getDiscountPrice());
//产品类目在订单详情里
orderVO.setOrderCategoryId(item.getOrderCategoryId());
orderVO.setOrderCategoryName(item.getOrderCategoryName());
//todo 服务保障列表 properties 服务内容 serveContent
});
//服务信息
orderItems.stream().filter(a->StrUtil.isNotBlank(a.getServeInfo())).findAny().ifPresent(a -> {
orderVO.setTradeServeInfo(JSON.parseObject(a.getServeInfo()));
orderVO.setTradeExtendServeInfo(JSON.parseArray(a.getServeExtInfo()));
orderVO.setTradeExtendCostInfo(JSON.parseArray(a.getPriceExtInfo()));
});
//保障时间 保障状态 如果状态是已完成 则
if (Objects.equals(order.getOrderStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) {
LocalDateTime finishTime = order.getFinishTime();
Long between = LocalDateTimeUtils.between(finishTime);
orderVO.setPropertyStatus(between >= 7 ? 0 : 1);
orderVO.setPropertyTime(LocalDateTimeUtils.getPlusDay(7));
}
return orderVO;
}
TradeOrderDetailRespVO convert2(TradeOrderDO order, List<TradeOrderItemDO> items, List<TradeOrderLogDO> statusList);
//endregion
//region修改订单
TradeOrderDO convert(TradeOrderRemarkReqVO reqVO);
//endregion
//region 订单快照
TradeOrderFastPhotoRespVo convert(TradeOrderItemDO itemDO, ProdDO prodDO);
//endregion
@Mappings({
@Mapping(target = "id", ignore = true),
@Mapping(source = "userId", target = "userId"),
@@ -123,27 +171,6 @@ public interface TradeOrderConvert {
return createReqDTO;
}
// ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
default TradeOrderDetailRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
List<TradeOrderLogDO> orderLogs,
MemberUserRespDTO user, MemberUserRespDTO brokerageUser) {
TradeOrderDetailRespVO orderVO = convert2(order, orderItems);
// // 处理收货地址
// orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));
// // 处理用户信息
// orderVO.setUser(convert(user));
// orderVO.setBrokerageUser(convert(brokerageUser));
// // 处理日志
// orderVO.setLogs(convertList03(orderLogs));
return orderVO;
}
List<TradeOrderDetailRespVO> convertList03(List<TradeOrderLogDO> orderLogs);
TradeOrderDetailRespVO convert2(TradeOrderDO order, List<TradeOrderItemDO> items);
MemberUserRespVO convert(MemberUserRespDTO bean);
default PageResult<AppTradeOrderPageItemRespVO> convertPage02(PageResult<TradeOrderDO> pageResult,
@@ -159,8 +186,6 @@ public interface TradeOrderConvert {
AppTradeOrderPageItemRespVO convert02(TradeOrderDO order, List<TradeOrderItemDO> items);
// AppProductPropertyValueDetailRespVO convert02(ProductPropertyValueDetailRespDTO bean);
default AppTradeOrderDetailRespVO convert02(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
TradeOrderProperties tradeOrderProperties,
DeliveryExpressDO express) {
@@ -192,10 +217,6 @@ public interface TradeOrderConvert {
@Mapping(target = "anonymous", source = "createReqVO.anonymous"),
@Mapping(target = "userId", source = "tradeOrderItemDO.userId")
})
// ProductCommentCreateReqDTO convert04(AppTradeOrderItemCommentCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItemDO);
//
// TradePriceCalculateReqBO convert(AppTradeOrderSettlementReqVO settlementReqVO);
//
default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
List<CartDO> cartList) {
TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO().setUserId(userId)
@@ -248,7 +269,7 @@ public interface TradeOrderConvert {
TradeOrderDO convert(TradeOrderUpdatePriceReqVO reqVO);
TradeOrderDO convert(TradeOrderRemarkReqVO reqVO);
default BrokerageAddReqBO convert(MemberUserRespDTO user, TradeOrderItemDO item,
ProductSpuRespDTO spu, ProductSkuRespDTO sku) {
@@ -262,7 +283,4 @@ public interface TradeOrderConvert {
return bo;
}
@Named("convertList04")
List<TradeOrderRespDTO> convertList04(List<TradeOrderDO> list);
}

View File

@@ -32,14 +32,7 @@ public class TradeOrderDO extends BaseDO {
* 订单流水号
*/
private String orderNum;
/**
* 订单类目id
*/
private Long orderCategoryId;
/**
* 订单类目名称
*/
private String orderCategoryName;
/**
* 订单类型 (枚举 TradeOrderTypeEnum)
*/
@@ -112,14 +105,7 @@ public class TradeOrderDO extends BaseDO {
* 实收金额(总),单位:分
*/
private Integer livePrice;
/**
* 预约类型1预约 2 加急)
*/
private Boolean subType;
/**
* 预约时间
*/
private LocalDateTime subTime;
/**
* 支付订单编号
*/

View File

@@ -60,6 +60,22 @@ public class TradeOrderItemDO extends BaseDO {
* 商品 SKU 名称
*/
private String skuName;
/**
* 店铺id
*/
private Long shopId;
/**
* 店铺名称
*/
private String shopName;
/**
* 订单类目id
*/
private Long orderCategoryId;
/**
* 订单类目名称
*/
private String orderCategoryName;
/**
* 商品图片
*/
@@ -100,11 +116,36 @@ public class TradeOrderItemDO extends BaseDO {
* 实收金额(总),单位:分
*/
private Integer livePrice;
/**
* 退款金额(总),单位:分
*/
private Integer refundPrice;
/**
* 退款状态
*/
private Integer refundStatus;
/**
* 退款时间
*/
private LocalDateTime refundTime;
/**
* 预约类型1预约 2 加急)
*/
private Boolean subType;
/**
* 预约时间
*/
private LocalDateTime subTime;
/**
* 服务地址
*/
private LocalDateTime serveAddress;
private String serveAddress;
/**
* 服务内容
*/
private String serveContent;
/**
* 属性数组
*/

View File

@@ -1,6 +1,5 @@
package com.tashow.cloud.trade.dal.dataobject.order;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.tashow.cloud.common.enums.UserTypeEnum;
@@ -13,7 +12,7 @@ import lombok.*;
*
* @author 陈賝
*/
@TableName("trade_order_log")
@TableName("tz_trade_order_log")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)

View File

@@ -0,0 +1,44 @@
package com.tashow.cloud.trade.dal.dataobject.order;
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.time.LocalDateTime;
/**
* 订单预约时间修改记录 DO
*
* @author 芋道源码
*/
@TableName("tz_trade_sub_log")
@KeySequence("tz_trade_sub_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TradeOrderSubLogDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 用户编号
*/
private Long userId;
/**
* 订单号
*/
private Long orderId;
/**
* 预约时间
*/
private LocalDateTime subTime;
}

View File

@@ -32,12 +32,9 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
default PageResult<TradeOrderDO> selectPage(TradeOrderPageReqVO reqVO, Set<Long> userIds) {
return selectPage(reqVO, new LambdaQueryWrapperX<TradeOrderDO>()
.likeIfPresent(TradeOrderDO::getMerchantName, reqVO.getMerchantName())
.eqIfPresent(TradeOrderDO::getOrderCategoryId, reqVO.getOrderCategoryId())
.eqIfPresent(TradeOrderDO::getOrderTerminal, reqVO.getOrderTerminal())
.eqIfPresent(TradeOrderDO::getFinanceStatus, reqVO.getFinanceStatus())
.eqIfPresent(TradeOrderDO::getAfterSaleStatus, reqVO.getAfterSaleStatus())
.eqIfPresent(TradeOrderDO::getSubType, reqVO.getSubType())
.betweenIfPresent(TradeOrderDO::getSubTime, reqVO.getSubTime())
.betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime())
.and(StrUtil.isNotBlank(reqVO.getProdSearch()),
qw->qw.like(TradeOrderDO::getOrderNum, reqVO.getProdSearch())
@@ -49,15 +46,13 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
.orderByDesc(TradeOrderDO::getId));
}
// TODO @疯狂:如果用 map 返回,要不这里直接用 TradeOrderSummaryRespVO 返回?也算合理,就当 sql 查询出这么个玩意~~
default List<Map<String, Object>> selectOrderSummaryGroupByRefundStatus(TradeOrderPageReqVO reqVO, Set<Long> userIds) {
default List<Map<String, Object>> selectOrderSummary() {
return selectMaps(new MPJLambdaWrapperX<TradeOrderDO>()
.selectAs(TradeOrderDO::getRefundStatus, TradeOrderDO::getRefundStatus) // 售后状态
.selectCount(TradeOrderDO::getId, "count") // 售后状态对应的数量
.selectSum(TradeOrderDO::getPayPrice, "price") // 售后状态对应的支付金额
.inIfPresent(TradeOrderDO::getUserId, userIds)
.betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime())
.groupBy(TradeOrderDO::getRefundStatus)); // 按售后状态分组
.selectCount(TradeOrderDO::getId, "count")
.selectSum(TradeOrderDO::getPayPrice, "payPrice")
.selectSum(TradeOrderDO::getLivePrice, "livePrice")
.groupBy(TradeOrderDO::getId)
);
}
default PageResult<TradeOrderDO> selectPage(AppTradeOrderPageReqVO reqVO, Long userId) {

View File

@@ -0,0 +1,15 @@
package com.tashow.cloud.trade.dal.mysql.order;
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderSubLogDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单预约时间修改记录 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface TradeOrderSubLogMapper extends BaseMapperX<TradeOrderSubLogDO> {
}

View File

@@ -0,0 +1,176 @@
package com.tashow.cloud.trade.framework.order.core.utils;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tashow.cloud.common.util.json.JsonUtils;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class TransOrderByTempUtils {
private final static String tempUrl = "temp/order_serve_info.json";
public static JSONObject trans(TradeOrderItemDO item) {
JSONObject result = new JSONObject();
String json = ResourceUtil.readUtf8Str(tempUrl);
//解析json
JSONObject orderInfo = JSON.parseObject(json);
JSONArray serveInfoTempObj = orderInfo.getJSONArray("serveInfo");
JSONArray serveExtInfoTempObj = orderInfo.getJSONArray("selectedServeInfo");
JSONArray priceExtInfoTempObj = orderInfo.getJSONArray("priceExtInfo");
//获取服务信息
result.put("serveInfo", getServeInfo(serveInfoTempObj, item.getServeInfo()));
//可选服务
result.put("selectedServeInfo", getServeInfo(serveExtInfoTempObj, item.getServeExtInfo()));
//服务附加费
result.put("priceExtInfo", getServeInfo(priceExtInfoTempObj, item.getPriceExtInfo()));
return result;
}
private static List<List<JSONObject>> getServeInfo(JSONArray tempObj, String valueInfo) {
List<List<JSONObject>> result = new ArrayList<>();
//数据json [{},{}]
JSONArray valueInfoObj = JSON.parseArray(valueInfo);
for (Object val : valueInfoObj) {
List<JSONObject> valResult = new ArrayList<>();
Map valMap = JsonUtils.parseObject(val.toString(), Map.class);
if (valMap == null) {
continue;
}
//模版json
for (Object tem : tempObj) {
JSONObject resultMap = new JSONObject();
Map<String, Object> tempMap = JsonUtils.parseObject(tem.toString(), Map.class);
resultMap.put("key", MapUtil.getStr(tempMap, "key"));
resultMap.put("name", MapUtil.getStr(tempMap, "name"));
resultMap.put("value", valMap.get(MapUtil.getStr(tempMap, "key")));
resultMap.put("sort", MapUtil.getStr(tempMap, "sort"));
valResult.add(resultMap);
}
result.add(valResult);
}
return result;
}
public static void main(String[] args) {
TradeOrderItemDO itemDO = new TradeOrderItemDO();
JSONArray tempObj = new JSONArray();
JSONObject orderInfo = new JSONObject();
orderInfo.put("petName", "aaa");
orderInfo.put("petType", "aaa");
orderInfo.put("weight", "aaa");
orderInfo.put("diedTime", "aaa");
orderInfo.put("diedReason", "aaa");
orderInfo.put("remainsPhoneUrl", "aaa");
orderInfo.put("subType", "aaa");
orderInfo.put("changeRule", "aaa");
orderInfo.put("subOrder", "aaa");
orderInfo.put("userRemark", "aaa");
orderInfo.put("merchantRemark", "aaa");
orderInfo.put("pickUpAddress", "aaa");
orderInfo.put("sendAddress", "aaa");
tempObj.add(orderInfo);
orderInfo = new JSONObject();
orderInfo.put("petName", "bbb");
orderInfo.put("petType", "bbb");
orderInfo.put("weight", "bbb");
orderInfo.put("diedTime", "bbb");
orderInfo.put("diedReason", "bbb");
orderInfo.put("remainsPhoneUrl", "bbb");
orderInfo.put("subType", "bbb");
orderInfo.put("changeRule", "bbb");
orderInfo.put("subOrder", "bbb");
orderInfo.put("userRemark", "bbb");
orderInfo.put("merchantRemark", "bbb");
orderInfo.put("pickUpAddress", "bbb");
orderInfo.put("sendAddress", "bbb");
tempObj.add(orderInfo);
String jsonString = JSON.toJSONString(tempObj);
System.out.println("serveInfo:" + jsonString);
itemDO.setServeInfo(jsonString);
JSONArray tempObj1 = new JSONArray();
JSONObject extraInfo = new JSONObject();
extraInfo.put("serveUrl", "aaa");
extraInfo.put("serveName", "aaa");
extraInfo.put("serveDesc", "aaa");
extraInfo.put("prodType", "aaa");
extraInfo.put("parentActive", "aaa");
extraInfo.put("count", "aaa");
extraInfo.put("price", "aaa");
extraInfo.put("handPrice", "aaa");
extraInfo.put("totalPrice", "aaa");
extraInfo.put("discountPrice", "aaa");
extraInfo.put("payPrice", "aaa");
extraInfo.put("refundMoney", "aaa");
extraInfo.put("refundCount", "aaa");
extraInfo.put("deliveryType", "aaa");
extraInfo.put("receiptUser", "aaa");
extraInfo.put("userMobile", "aaa");
extraInfo.put("receiveAddress", "aaa");
tempObj1.add(extraInfo);
extraInfo = new JSONObject();
extraInfo.put("serveUrl", "bbb");
extraInfo.put("serveName", "bbb");
extraInfo.put("serveDesc", "bbb");
extraInfo.put("prodType", "bbb");
extraInfo.put("parentActive", "bbb");
extraInfo.put("count", "bbb");
extraInfo.put("price", "bbb");
extraInfo.put("handPrice", "bbb");
extraInfo.put("totalPrice", "bbb");
extraInfo.put("discountPrice", "bbb");
extraInfo.put("payPrice", "bbb");
extraInfo.put("refundMoney", "bbb");
extraInfo.put("refundCount", "bbb");
extraInfo.put("deliveryType", "bbb");
extraInfo.put("receiptUser", "bbb");
extraInfo.put("userMobile", "bbb");
extraInfo.put("receiveAddress", "bbb");
tempObj1.add(extraInfo);
String jsonString1 = JSON.toJSONString(tempObj1);
System.out.println("selected:" + jsonString1);
itemDO.setServeExtInfo(jsonString1);
JSONArray priceExtObj = new JSONArray();
JSONObject priceExtInfo = new JSONObject();
priceExtInfo.put("costName", "aaa");
priceExtInfo.put("serveArea", "aaa");
priceExtInfo.put("targetArea", "aaa");
priceExtInfo.put("chargeType", "aaa");
priceExtInfo.put("respMode", "aaa");
priceExtInfo.put("respTime", "aaa");
priceExtInfo.put("weight", "aaa");
priceExtInfo.put("chargeTime", "aaa");
priceExtInfo.put("totalPrice", "aaa");
priceExtInfo.put("discountPrice", "aaa");
priceExtInfo.put("payPrice", "aaa");
priceExtInfo.put("refundPrice", "aaa");
priceExtObj.add(priceExtInfo);
priceExtInfo = new JSONObject();
priceExtInfo.put("costName", "bbb");
priceExtInfo.put("serveArea", "bbb");
priceExtInfo.put("targetArea", "bbb");
priceExtInfo.put("chargeType", "bbb");
priceExtInfo.put("respMode", "bbb");
priceExtInfo.put("respTime", "bbb");
priceExtInfo.put("weight", "bbb");
priceExtInfo.put("chargeTime", "bbb");
priceExtInfo.put("totalPrice", "bbb");
priceExtInfo.put("discountPrice", "bbb");
priceExtInfo.put("payPrice", "bbb");
priceExtInfo.put("refundPrice", "bbb");
priceExtObj.add(priceExtInfo);
String jsonString2 = JSON.toJSONString(priceExtObj);
System.out.println("price:" + jsonString2);
itemDO.setPriceExtInfo(jsonString2);
Map<String, Object> trans = trans(itemDO);
System.out.println(JSON.toJSONString(trans));
}
}

View File

@@ -6,6 +6,7 @@ import com.tashow.cloud.payapi.api.order.PayOrderApi;
import com.tashow.cloud.payapi.api.refund.PayRefundApi;
import com.tashow.cloud.payapi.api.transfer.PayTransferApi;
import com.tashow.cloud.payapi.api.wallet.PayWalletApi;
import com.tashow.cloud.productapi.api.product.ProdApi;
import com.tashow.cloud.systemapi.api.notify.NotifyMessageSendApi;
import com.tashow.cloud.systemapi.api.social.SocialClientApi;
import com.tashow.cloud.systemapi.api.social.SocialUserApi;
@@ -16,6 +17,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = {
MemberUserApi.class, MemberAddressApi.class,
ProdApi.class,
PayOrderApi.class, PayRefundApi.class, PayTransferApi.class, PayWalletApi.class,
AdminUserApi.class, NotifyMessageSendApi.class, SocialClientApi.class, SocialUserApi.class
})

View File

@@ -7,6 +7,7 @@ import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderSummaryRespVO;
import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderSubLogDO;
import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import com.tashow.cloud.tradeapi.enums.order.TradeOrderTypeEnum;
@@ -70,10 +71,9 @@ public interface TradeOrderQueryService {
/**
* 获得订单统计
*
* @param reqVO 请求参数
* @return 订单统计
*/
TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO);
TradeOrderSummaryRespVO getOrderSummary();
/**
* 【会员】获得交易订单分页
@@ -158,4 +158,10 @@ public interface TradeOrderQueryService {
*/
List<TradeOrderItemDO> getOrderItemListByOrderId(Collection<Long> orderIds);
/**
* 获取订单预约时间修改列表
* @param id
* @return
*/
List<TradeOrderSubLogDO> getSubTimeLogList(Long id);
}

View File

@@ -1,9 +1,9 @@
package com.tashow.cloud.trade.service.order;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderPageReqVO;
import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderSummaryRespVO;
@@ -11,14 +11,15 @@ import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderSubLogDO;
import com.tashow.cloud.trade.dal.mysql.order.TradeOrderItemMapper;
import com.tashow.cloud.trade.dal.mysql.order.TradeOrderMapper;
import com.tashow.cloud.trade.dal.mysql.order.TradeOrderSubLogMapper;
import com.tashow.cloud.trade.dal.redis.RedisKeyConstants;
import com.tashow.cloud.trade.framework.delivery.core.client.ExpressClientFactory;
import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import com.tashow.cloud.trade.service.delivery.DeliveryExpressService;
import com.tashow.cloud.tradeapi.enums.order.TradeOrderRefundStatusEnum;
import com.tashow.cloud.tradeapi.enums.order.TradeOrderStatusEnum;
import com.tashow.cloud.tradeapi.enums.order.TradeOrderTypeEnum;
import jakarta.annotation.Resource;
@@ -48,6 +49,8 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
private TradeOrderMapper tradeOrderMapper;
@Resource
private TradeOrderItemMapper tradeOrderItemMapper;
@Resource
private TradeOrderSubLogMapper tradeOrderSubLogMapper;
@Resource
private DeliveryExpressService deliveryExpressService;
@@ -118,27 +121,12 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
}
@Override
public TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO) {
// 根据用户查询条件构建用户编号列表
Set<Long> userIds = buildQueryConditionUserIds(reqVO);
if (userIds == null) { // 没查询到用户,说明肯定也没他的订单
return new TradeOrderSummaryRespVO();
}
// 查询每个售后状态对应的数量、金额
List<Map<String, Object>> list = tradeOrderMapper.selectOrderSummaryGroupByRefundStatus(reqVO, userIds);
TradeOrderSummaryRespVO vo = new TradeOrderSummaryRespVO().setAfterSaleCount(0L).setAfterSalePrice(0L);
for (Map<String, Object> map : list) {
Long count = MapUtil.getLong(map, "count", 0L);
Long price = MapUtil.getLong(map, "price", 0L);
// 未退款的计入订单,部分退款、全部退款计入售后
if (TradeOrderRefundStatusEnum.NONE.getStatus().equals(MapUtil.getInt(map, "refundStatus"))) {
vo.setOrderCount(count).setOrderPayPrice(price);
} else {
vo.setAfterSaleCount(vo.getAfterSaleCount() + count).setAfterSalePrice(vo.getAfterSalePrice() + price);
}
}
return vo;
public TradeOrderSummaryRespVO getOrderSummary() {
List<Map<String, Object>> list = tradeOrderMapper.selectOrderSummary();
return new TradeOrderSummaryRespVO()
.setOrderCount(list.stream().mapToLong(x -> Long.parseLong(x.get("count").toString())).sum())
.setLivePrice(list.stream().mapToLong(x -> Long.parseLong(x.get("livePrice").toString())).sum())
.setPayPrice(list.stream().mapToLong(x -> Long.parseLong(x.get("payPrice").toString())).sum());
}
@Override
@@ -255,4 +243,8 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
return SpringUtil.getBean(getClass());
}
@Override
public List<TradeOrderSubLogDO> getSubTimeLogList(Long id) {
return tradeOrderSubLogMapper.selectList(new LambdaQueryWrapper<TradeOrderSubLogDO>().eq(TradeOrderSubLogDO::getOrderId,id));
}
}

View File

@@ -0,0 +1,14 @@
package com.tashow.cloud.trade.service.order;
import com.baomidou.mybatisplus.extension.service.IService;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderSubLogDO;
/**
* 订单预约时间修改记录 Service 接口
*
* @author 芋道源码
*/
public interface TradeOrderSubLogService extends IService<TradeOrderSubLogDO> {
}

View File

@@ -0,0 +1,20 @@
package com.tashow.cloud.trade.service.order;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderSubLogDO;
import com.tashow.cloud.trade.dal.mysql.order.TradeOrderSubLogMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
/**
* 订单预约时间修改记录 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class TradeOrderSubLogServiceImpl extends ServiceImpl<TradeOrderSubLogMapper, TradeOrderSubLogDO> implements TradeOrderSubLogService {
}

View File

@@ -41,6 +41,19 @@ public interface TradeOrderUpdateService {
*/
TradeOrderDO createOrder(Long userId, AppTradeOrderCreateReqVO createReqVO);
/**
* 确认接单
* @param id
*/
void acceptConfirmOrderByAdmin(Long id);
/**
* 服务上报
* @param id
*/
void reportServeOrderByAdmin(Long id);
/**
* 更新交易订单已支付
*
@@ -89,6 +102,12 @@ public interface TradeOrderUpdateService {
* @param id 订单编号
*/
void cancelOrderByMember(Long userId, Long id);
/**
* 【管理员】取消交易订单
*
* @param id 订单编号
*/
boolean cancelOrderByAdmin(Long id);
/**
* 【系统】自动取消订单
@@ -218,4 +237,5 @@ public interface TradeOrderUpdateService {
*/
void updateOrderGiveCouponIds(Long userId, Long orderId, List<Long> giveCouponIds);
}

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