From a34b9a6418a8874dc0851cb4e19d189339dd5133 Mon Sep 17 00:00:00 2001 From: Administrator <15274802129@163.com> Date: Mon, 04 Aug 2025 15:23:35 +0800 Subject: [PATCH] feat(ai): 添加 AI 用户答题功能 --- src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerService.java | 3 src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java | 2 src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerServiceImpl.java | 50 ++++ src/main/java/cc/mrbird/febs/mall/entity/MallMember.java | 1 src/main/java/cc/mrbird/febs/ai/controller/memberAnswer/ApiMemberAnswerController.java | 43 +++ src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java | 4 src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerItemServiceImpl.java | 4 src/main/java/cc/mrbird/febs/ai/res/memberAnswer/ApiMemberAnswerVo.java | 51 ++++ src/main/java/cc/mrbird/febs/ai/res/productQuestionItem/ApiMemberAnswerItemVo.java | 37 +++ src/main/java/cc/mrbird/febs/ai/req/memberAnswer/ApiMemberAnswerDto.java | 19 + src/main/java/cc/mrbird/febs/ai/utils/UUID.java | 492 +++++++++++++++++++++++++++++++++++++++++ src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java | 10 12 files changed, 714 insertions(+), 2 deletions(-) diff --git a/src/main/java/cc/mrbird/febs/ai/controller/memberAnswer/ApiMemberAnswerController.java b/src/main/java/cc/mrbird/febs/ai/controller/memberAnswer/ApiMemberAnswerController.java new file mode 100644 index 0000000..33cde54 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/controller/memberAnswer/ApiMemberAnswerController.java @@ -0,0 +1,43 @@ +package cc.mrbird.febs.ai.controller.memberAnswer; + +import cc.mrbird.febs.ai.req.memberAnswer.ApiMemberAnswerDto; +import cc.mrbird.febs.ai.req.product.ApiProductInfoDto; +import cc.mrbird.febs.ai.res.memberAnswer.ApiMemberAnswerVo; +import cc.mrbird.febs.ai.res.product.ApiProductInfoVo; +import cc.mrbird.febs.ai.service.AiMemberAnswerService; +import cc.mrbird.febs.common.entity.FebsResponse; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author Administrator + */ +@Slf4j +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/api/ai/memberAnswer") +@Api(value = "ApiMemberAnswerController", tags = "AI-用户答题") +public class ApiMemberAnswerController { + + private final AiMemberAnswerService apiMemberAnswerService; + + @ApiOperation(value = "开始练习", notes = "开始练习") + @ApiResponses({ + @ApiResponse(code = 200, message = "success", response = ApiMemberAnswerVo.class) + }) + @PostMapping(value = "/start") + public FebsResponse start(@RequestBody @Validated ApiMemberAnswerDto dto) { + + return apiMemberAnswerService.startAnswer(dto); + } +} diff --git a/src/main/java/cc/mrbird/febs/ai/req/memberAnswer/ApiMemberAnswerDto.java b/src/main/java/cc/mrbird/febs/ai/req/memberAnswer/ApiMemberAnswerDto.java new file mode 100644 index 0000000..5dcb15b --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/req/memberAnswer/ApiMemberAnswerDto.java @@ -0,0 +1,19 @@ +package cc.mrbird.febs.ai.req.memberAnswer; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author Administrator + */ +@Data +@ApiModel(value = "ApiMemberAnswerDto", description = "参数") +public class ApiMemberAnswerDto { + + @NotBlank(message = "ID不能为空") + @ApiModelProperty(value = "产品ID", example = "10") + private String id; +} diff --git a/src/main/java/cc/mrbird/febs/ai/res/memberAnswer/ApiMemberAnswerVo.java b/src/main/java/cc/mrbird/febs/ai/res/memberAnswer/ApiMemberAnswerVo.java new file mode 100644 index 0000000..3306a4e --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/res/memberAnswer/ApiMemberAnswerVo.java @@ -0,0 +1,51 @@ +package cc.mrbird.febs.ai.res.memberAnswer; + +import cc.mrbird.febs.ai.res.productQuestionItem.ApiMemberAnswerItemVo; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * @author Administrator + */ +@Data +@ApiModel(value = "ApiMemberAnswerVo", description = "参数") +public class ApiMemberAnswerVo { + + + @ApiModelProperty(value = "产品名称") + private String name; + + + @ApiModelProperty(value = "题目数量") + private String questionCnt; + + + @ApiModelProperty(value = "答题数量") + private String questionDoneCnt; + + + @ApiModelProperty(value = "答对数量") + private String correctCnt; + + + /** + * 题目 + */ + @ApiModelProperty(value = "题目") + private String title; + + /** + * 难度:1-简单,2-中等,3-困难 + */ + @ApiModelProperty(value = "难度:1-简单,2-中等,3-困难") + private Integer difficulty; + + /** + * 答案 + */ + @ApiModelProperty(value = "答案") + private List<ApiMemberAnswerItemVo> answer; +} diff --git a/src/main/java/cc/mrbird/febs/ai/res/productQuestionItem/ApiMemberAnswerItemVo.java b/src/main/java/cc/mrbird/febs/ai/res/productQuestionItem/ApiMemberAnswerItemVo.java new file mode 100644 index 0000000..a8517f4 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/res/productQuestionItem/ApiMemberAnswerItemVo.java @@ -0,0 +1,37 @@ +package cc.mrbird.febs.ai.res.productQuestionItem; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author Administrator + */ +@Data +@ApiModel(value = "ApiMemberAnswerItemVo", description = "参数") +public class ApiMemberAnswerItemVo { + + /** + * 答案 + */ + @ApiModelProperty(value = "答案ID") + private String id; + + /** + * 答案 + */ + @ApiModelProperty(value = "答案") + private String answer; + + /** + * 是否是正确答案 0-否 1-是 + */ + @ApiModelProperty(value = "是否是正确答案 0-否 1-是") + private Integer correctAnswer; + + /** + * 答案解析 + */ + @ApiModelProperty(value = "答案解析") + private String answerAnalysis; +} diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerService.java b/src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerService.java index e07f037..0dfd29a 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerService.java +++ b/src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerService.java @@ -1,6 +1,8 @@ package cc.mrbird.febs.ai.service; import cc.mrbird.febs.ai.entity.AiMemberAnswer; +import cc.mrbird.febs.ai.req.memberAnswer.ApiMemberAnswerDto; +import cc.mrbird.febs.common.entity.FebsResponse; import com.baomidou.mybatisplus.extension.service.IService; import java.util.List; @@ -13,4 +15,5 @@ public interface AiMemberAnswerService extends IService<AiMemberAnswer> { + FebsResponse startAnswer(ApiMemberAnswerDto dto); } diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java b/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java index 57871fa..b73ceff 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java +++ b/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java @@ -1,6 +1,7 @@ package cc.mrbird.febs.ai.service; import cc.mrbird.febs.ai.entity.AiProductQuestion; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.IService; import java.util.List; @@ -13,4 +14,5 @@ public interface AiProductQuestionService extends IService<AiProductQuestion> { + List<AiProductQuestion> getListByQuery(LambdaQueryWrapper<AiProductQuestion> questionLambdaQueryWrapper); } diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerItemServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerItemServiceImpl.java index 9c74cec..39ad40a 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerItemServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerItemServiceImpl.java @@ -1,8 +1,10 @@ package cc.mrbird.febs.ai.service.impl; import cc.mrbird.febs.ai.entity.AiMemberAnswerItem; +import cc.mrbird.febs.ai.entity.AiProduct; import cc.mrbird.febs.ai.mapper.AiMemberAnswerItemMapper; import cc.mrbird.febs.ai.service.AiMemberAnswerItemService; +import cc.mrbird.febs.ai.service.AiProductService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; @@ -24,6 +26,4 @@ public class AiMemberAnswerItemServiceImpl extends ServiceImpl<AiMemberAnswerItemMapper, AiMemberAnswerItem> implements AiMemberAnswerItemService { private final AiMemberAnswerItemMapper aiMemberAnswerItemMapper; - - } diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerServiceImpl.java index 4ece58c..ede9b9c 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerServiceImpl.java @@ -1,9 +1,22 @@ package cc.mrbird.febs.ai.service.impl; import cc.mrbird.febs.ai.entity.AiMemberAnswer; +import cc.mrbird.febs.ai.entity.AiMemberAnswerItem; +import cc.mrbird.febs.ai.entity.AiProduct; +import cc.mrbird.febs.ai.entity.AiProductQuestion; import cc.mrbird.febs.ai.mapper.AiMemberAnswerMapper; +import cc.mrbird.febs.ai.req.memberAnswer.ApiMemberAnswerDto; +import cc.mrbird.febs.ai.res.memberAnswer.ApiMemberAnswerVo; +import cc.mrbird.febs.ai.service.AiMemberAnswerItemService; import cc.mrbird.febs.ai.service.AiMemberAnswerService; +import cc.mrbird.febs.ai.service.AiProductQuestionService; +import cc.mrbird.febs.ai.service.AiProductService; +import cc.mrbird.febs.common.entity.FebsResponse; +import cc.mrbird.febs.common.utils.LoginUserUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -24,6 +37,43 @@ public class AiMemberAnswerServiceImpl extends ServiceImpl<AiMemberAnswerMapper, AiMemberAnswer> implements AiMemberAnswerService { private final AiMemberAnswerMapper aiMemberAnswerMapper; + private final AiMemberAnswerItemService aiMemberAnswerItemService; + private final AiProductService aiProductService; + private final AiProductQuestionService aiProductQuestionService; + @Override + public FebsResponse startAnswer(ApiMemberAnswerDto dto) { + String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid(); + + String productId = dto.getId(); + AiProduct aiProduct = aiProductService.getById(productId); + if(ObjectUtil.isNull(aiProduct)){ + throw new RuntimeException("产品不存在"); + } + //验证产品分类下是否有题目 + String productCategoryId = aiProduct.getProductCategoryId(); + LambdaQueryWrapper<AiProductQuestion> questionLambdaQueryWrapper = Wrappers.lambdaQuery(AiProductQuestion.class); + questionLambdaQueryWrapper.eq(AiProductQuestion::getProductCategoryId,productCategoryId); + questionLambdaQueryWrapper.eq(AiProductQuestion::getState,1); + questionLambdaQueryWrapper.last("limit 1"); + List<AiProductQuestion> aiProductQuestions = aiProductQuestionService.getListByQuery(questionLambdaQueryWrapper); + if(CollUtil.isEmpty(aiProductQuestions)){ + // todo 没有题目采用AI生成题目 + throw new RuntimeException("产品没有题目"); + } + ApiMemberAnswerVo apiMemberAnswerVo = new ApiMemberAnswerVo(); + AiMemberAnswer aiMemberAnswer = new AiMemberAnswer(); + aiMemberAnswer.setProductId(aiProduct.getId()); + aiMemberAnswer.setMemberId(memberUuid); + aiMemberAnswer.setQuestionCnt(aiProduct.getQuestionCount()); + aiMemberAnswer.setScore(0); + aiMemberAnswer.setCorrectCnt(0); + aiMemberAnswer.setState(0); + aiMemberAnswerMapper.insert(aiMemberAnswer); + +// apiMemberAnswerVo.setTitle(); + + return new FebsResponse().success().data(apiMemberAnswerVo); + } } diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java index 02dde7b..4c51f5a 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java @@ -26,4 +26,8 @@ private final AiProductQuestionMapper aiProductQuestionMapper; + @Override + public List<AiProductQuestion> getListByQuery(LambdaQueryWrapper<AiProductQuestion> questionLambdaQueryWrapper) { + return aiProductQuestionMapper.selectList(questionLambdaQueryWrapper); + } } diff --git a/src/main/java/cc/mrbird/febs/ai/utils/UUID.java b/src/main/java/cc/mrbird/febs/ai/utils/UUID.java new file mode 100644 index 0000000..e1e80bb --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/utils/UUID.java @@ -0,0 +1,492 @@ +package cc.mrbird.febs.ai.utils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author ruoyi + */ +public final class UUID implements java.io.Serializable, Comparable<UUID> +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static String getSimpleUUIDString() + { + return randomUUID(true).toString(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + * <p> + * 版本号具有以下含意: + * <ul> + * <li>1 基于时间的 UUID + * <li>2 DCE 安全 UUID + * <li>3 基于名称的 UUID + * <li>4 随机生成的 UUID + * </ul> + * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + * <p> + * 变体号具有以下含意: + * <ul> + * <li>0 为 NCS 向后兼容保留 + * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类 + * <li>6 保留,微软向后兼容 + * <li>7 保留供以后定义使用 + * </ul> + * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + * <p> + * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br> + * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + * <p> + * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br> + * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + * <p> + * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + * <p> + * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + * <p> + * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + * <p> + * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br> + * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + * <p> + * UUID 的字符串表示形式由此 BNF 描述: + * + * <pre> + * {@code + * UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node> + * time_low = 4*<hexOctet> + * time_mid = 2*<hexOctet> + * time_high_and_version = 2*<hexOctet> + * variant_and_sequence = 2*<hexOctet> + * node = 6*<hexOctet> + * hexOctet = <hexDigit><hexDigit> + * hexDigit = [0-9a-fA-F] + * } + * </pre> + * + * </blockquote> + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + * <p> + * UUID 的字符串表示形式由此 BNF 描述: + * + * <pre> + * {@code + * UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node> + * time_low = 4*<hexOctet> + * time_mid = 2*<hexOctet> + * time_high_and_version = 2*<hexOctet> + * variant_and_sequence = 2*<hexOctet> + * node = 6*<hexOctet> + * hexOctet = <hexDigit><hexDigit> + * hexDigit = [0-9a-fA-F] + * } + * </pre> + * + * </blockquote> + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (!isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (!isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (!isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (!isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + * <p> + * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + * <p> + * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException(e); + } + } + + /** + * 获取随机数生成器对象<br> + * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/src/main/java/cc/mrbird/febs/mall/entity/MallMember.java b/src/main/java/cc/mrbird/febs/mall/entity/MallMember.java index 06f3cd0..5ece26c 100644 --- a/src/main/java/cc/mrbird/febs/mall/entity/MallMember.java +++ b/src/main/java/cc/mrbird/febs/mall/entity/MallMember.java @@ -18,6 +18,7 @@ @TableName("mall_member") public class MallMember extends BaseEntity { + private String memberUuid; /** * 姓名 */ diff --git a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java index 978f6cd..2ff0da9 100644 --- a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java @@ -1,5 +1,8 @@ package cc.mrbird.febs.mall.service.impl; +import cc.mrbird.febs.ai.entity.AiMember; +import cc.mrbird.febs.ai.mapper.AiMemberMapper; +import cc.mrbird.febs.ai.utils.UUID; import cc.mrbird.febs.common.configure.FebsConfigure; import cc.mrbird.febs.common.entity.FebsResponse; import cc.mrbird.febs.common.enumerates.*; @@ -67,6 +70,7 @@ @RequiredArgsConstructor public class ApiMallMemberServiceImpl extends ServiceImpl<MallMemberMapper, MallMember> implements IApiMallMemberService { + private final AiMemberMapper aiMemberMapper; private final MallMemberWalletMapper mallMemberWalletMapper; private final ICommonService commonService; private final RedisUtils redisUtils; @@ -728,8 +732,14 @@ } this.baseMapper.insert(mallMember); + AiMember aiMember = new AiMember(); + aiMember.setId(UUID.getSimpleUUIDString()); + aiMember.setCreatedTime(new Date()); + aiMemberMapper.insert(aiMember); + mallMember = this.baseMapper.selectMemberByOpenId(openId); String inviteId = ShareCodeUtil.toSerialCode(mallMember.getId()); + mallMember.setMemberUuid(aiMember.getId()); mallMember.setInviteId(inviteId); this.baseMapper.updateById(mallMember); MallMemberWallet wallet = new MallMemberWallet(); -- Gitblit v1.9.1