瀏覽代碼

推荐服务基础架构提交

jintao.geng 1 月之前
當前提交
1a7760fdbe
共有 100 個文件被更改,包括 9732 次插入0 次删除
  1. 1 0
      README.md
  2. 80 0
      mango-application/pom.xml
  3. 32 0
      mango-application/src/main/java/com/mangoo/rating/recommend/RatingRecommendApplication.java
  4. 71 0
      mango-application/src/main/java/com/mangoo/rating/recommend/app/controller/AiChatController.java
  5. 157 0
      mango-application/src/main/java/com/mangoo/rating/recommend/aspectj/ApiLogAspect.java
  6. 130 0
      mango-application/src/main/java/com/mangoo/rating/recommend/aspectj/Global.java
  7. 23 0
      mango-application/src/main/java/com/mangoo/rating/recommend/config/AiAsyncConfig.java
  8. 131 0
      mango-application/src/main/resources/application-dev.yml
  9. 136 0
      mango-application/src/main/resources/application-prod.yml
  10. 347 0
      mango-application/src/main/resources/application.yml
  11. 751 0
      mango-application/src/main/resources/db/migration/V1.1_mongo_order_create.sql
  12. 11 0
      mango-application/src/main/resources/dozer/dozer-mapping.xml
  13. 21 0
      mango-application/src/main/resources/logback-fluentd.xml
  14. 61 0
      mango-application/src/main/resources/logback.xml
  15. 15 0
      mango-application/src/main/resources/mybatis/mybatis-config.xml
  16. 二進制
      mango-application/src/main/resources/tessdata/chi_sim.traineddata
  17. 二進制
      mango-application/src/main/resources/tessdata/eng.traineddata
  18. 11 0
      mango-application/src/test/java/com/mangoo/rating/recommend/test/RatingAppServiceTests.java
  19. 65 0
      mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/AgentLoopExecutorTest.java
  20. 47 0
      mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/AiChatServiceTest.java
  21. 56 0
      mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/ClaudeSseParserTest.java
  22. 56 0
      mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/OrderLogisticsQueryToolTest.java
  23. 55 0
      mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/SessionContextStoreTest.java
  24. 34 0
      mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/ToolRegistryTest.java
  25. 355 0
      mango-common/pom.xml
  26. 254 0
      mango-common/src/main/java/com/mangoo/rating/recommend/AjaxResult.java
  27. 239 0
      mango-common/src/main/java/com/mangoo/rating/recommend/BaseEntity.java
  28. 24 0
      mango-common/src/main/java/com/mangoo/rating/recommend/BaseParam.java
  29. 88 0
      mango-common/src/main/java/com/mangoo/rating/recommend/CharsetKit.java
  30. 1001 0
      mango-common/src/main/java/com/mangoo/rating/recommend/Convert.java
  31. 69 0
      mango-common/src/main/java/com/mangoo/rating/recommend/CxSelect.java
  32. 198 0
      mango-common/src/main/java/com/mangoo/rating/recommend/MsgResult.java
  33. 105 0
      mango-common/src/main/java/com/mangoo/rating/recommend/Mtree.java
  34. 92 0
      mango-common/src/main/java/com/mangoo/rating/recommend/StrFormatter.java
  35. 63 0
      mango-common/src/main/java/com/mangoo/rating/recommend/TreeEntity.java
  36. 60 0
      mango-common/src/main/java/com/mangoo/rating/recommend/Ztree.java
  37. 13 0
      mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/ChatMessage.java
  38. 12 0
      mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/ChatResult.java
  39. 4 0
      mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/Role.java
  40. 4 0
      mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/StreamCallback.java
  41. 11 0
      mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/ToolCall.java
  42. 11 0
      mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/ToolSpec.java
  43. 26 0
      mango-common/src/main/java/com/mangoo/rating/recommend/annotation/ApiLimitRule.java
  44. 40 0
      mango-common/src/main/java/com/mangoo/rating/recommend/annotation/ApiLog.java
  45. 163 0
      mango-common/src/main/java/com/mangoo/rating/recommend/annotation/Excel.java
  46. 33 0
      mango-common/src/main/java/com/mangoo/rating/recommend/annotation/RequiresPermission.java
  47. 12 0
      mango-common/src/main/java/com/mangoo/rating/recommend/annotation/ValueAspect.java
  48. 30 0
      mango-common/src/main/java/com/mangoo/rating/recommend/condition/MoldNumberQueryCondition.java
  49. 81 0
      mango-common/src/main/java/com/mangoo/rating/recommend/condition/OrderCardQueryCondition.java
  50. 47 0
      mango-common/src/main/java/com/mangoo/rating/recommend/condition/OrderQueryCondition.java
  51. 43 0
      mango-common/src/main/java/com/mangoo/rating/recommend/condition/OrderQueryPageCondition.java
  52. 38 0
      mango-common/src/main/java/com/mangoo/rating/recommend/condition/RecognizedInfoQueryCondition.java
  53. 23 0
      mango-common/src/main/java/com/mangoo/rating/recommend/condition/SysOperLogQueryCondition.java
  54. 26 0
      mango-common/src/main/java/com/mangoo/rating/recommend/condition/SysUserQueryCondition.java
  55. 27 0
      mango-common/src/main/java/com/mangoo/rating/recommend/condition/UserAddressQueryCondition.java
  56. 28 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/AiAssistantProperties.java
  57. 59 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/BaseConfig.java
  58. 33 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/CommonConfig.java
  59. 32 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/DynamicRedisConfig.java
  60. 59 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/Knife4jConfig.java
  61. 29 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/LimitRule.java
  62. 83 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/MetricsConfig.java
  63. 60 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/PermissionInterceptorConfig.java
  64. 343 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/RabbitMQConfig.java
  65. 50 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/RabbitPublisherConfirmConfig.java
  66. 289 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/RedisConfig.java
  67. 88 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/ServerModel.java
  68. 57 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/TencentCosConfig.java
  69. 32 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/MyThreadPoolTaskExecutor.java
  70. 51 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/RejectedPolicyExecution.java
  71. 53 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/ThreadFactoryConfig.java
  72. 69 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/ThreadMdcUtil.java
  73. 58 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/ThreadPoolBeanConfig.java
  74. 68 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/ThreadPoolBeanShutdownHandler.java
  75. 35 0
      mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/ThreadPoolConfig.java
  76. 53 0
      mango-common/src/main/java/com/mangoo/rating/recommend/constant/ActConstans.java
  77. 27 0
      mango-common/src/main/java/com/mangoo/rating/recommend/constant/AdConstans.java
  78. 29 0
      mango-common/src/main/java/com/mangoo/rating/recommend/constant/BusConstants.java
  79. 1003 0
      mango-common/src/main/java/com/mangoo/rating/recommend/constant/Constants.java
  80. 11 0
      mango-common/src/main/java/com/mangoo/rating/recommend/constant/DictConstants.java
  81. 72 0
      mango-common/src/main/java/com/mangoo/rating/recommend/constant/RedisKeyManager.java
  82. 33 0
      mango-common/src/main/java/com/mangoo/rating/recommend/constant/RetConstants.java
  83. 74 0
      mango-common/src/main/java/com/mangoo/rating/recommend/constant/ShiroConstants.java
  84. 39 0
      mango-common/src/main/java/com/mangoo/rating/recommend/constant/UserType.java
  85. 283 0
      mango-common/src/main/java/com/mangoo/rating/recommend/core/BaseController.java
  86. 81 0
      mango-common/src/main/java/com/mangoo/rating/recommend/core/service/BaseService.java
  87. 179 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/AppUserInfoDto.java
  88. 46 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/DictPropDTO.java
  89. 24 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/ExpressCourierNumDTO.java
  90. 18 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/ExpressInfoContextDTO.java
  91. 11 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/ExpressInfoDTO.java
  92. 35 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/ExpressInfoDetailDTO.java
  93. 166 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/InDto.java
  94. 194 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/OutDTO.java
  95. 103 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/ReceiveInfoDTO.java
  96. 40 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/RecognizedInfoQueryDTO.java
  97. 47 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/TaskResultDTO.java
  98. 54 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/UserInfo.java
  99. 23 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/card/CardCameraCacheDTO.java
  100. 28 0
      mango-common/src/main/java/com/mangoo/rating/recommend/dto/card/CardCreateDTO.java

+ 1 - 0
README.md

@@ -0,0 +1 @@
+卡牌评级小程序 推荐服务

+ 80 - 0
mango-application/pom.xml

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.poyee</groupId>
+        <artifactId>ratingRecommend</artifactId>
+        <version>1.0.0</version>
+    </parent>
+
+	<artifactId>mango-application</artifactId>
+	<version>1.0.0</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.poyee</groupId>
+            <artifactId>mango-domain</artifactId>
+        </dependency>
+
+        <!-- logback if解析 -->
+        <dependency>
+            <groupId>org.codehaus.janino</groupId>
+            <artifactId>janino</artifactId>
+            <version>3.0.12</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sndyuk</groupId>
+            <artifactId>logback-more-appenders</artifactId>
+            <version>1.8.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.fluentd</groupId>
+            <artifactId>fluent-logger</artifactId>
+            <version>0.3.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- 添加Spring Boot测试依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- flyway -->
+        <dependency>
+            <groupId>org.flywaydb</groupId>
+            <artifactId>flyway-core</artifactId>
+        </dependency>
+
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.1.1.RELEASE</version>
+                <configuration>
+                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
+                    <includeSystemScope>true</includeSystemScope>
+                </configuration>
+
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 32 - 0
mango-application/src/main/java/com/mangoo/rating/recommend/RatingRecommendApplication.java

@@ -0,0 +1,32 @@
+package com.mangoo.rating.recommend;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * APP服务启动类
+ * @author: gengjintao
+ * @date: 2026/01/07
+ */
+
+@MapperScan("com.mangoo.rating.**.mapper")
+@EnableCaching
+@EnableAspectJAutoProxy(proxyTargetClass = true)
+@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, SecurityAutoConfiguration.class},scanBasePackages = {"com.mangoo.rating"})
+@EnableScheduling
+@EnableFeignClients(basePackages = "com.mangoo.rating.recommend.client.feign")
+public class RatingRecommendApplication {
+    public static void main(String[] args) {
+        System.out.println("RatingRecommendApplication start...");
+        SpringApplication.run(RatingRecommendApplication.class, args);
+        System.out.println("RatingRecommendApplication end...");
+    }
+}
+

+ 71 - 0
mango-application/src/main/java/com/mangoo/rating/recommend/app/controller/AiChatController.java

@@ -0,0 +1,71 @@
+package com.mangoo.rating.recommend.app.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.mangoo.rating.recommend.ai.service.AiChatService;
+import com.mangoo.rating.recommend.request.ai.AiChatRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import javax.annotation.Resource;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+
+/** 小程序 AI 会话入口(SSE 流式打字机输出) */
+@Slf4j
+@RestController
+@RequestMapping("/api/ai")
+@RequiredArgsConstructor
+public class AiChatController {
+
+    private final AiChatService aiChatService;
+
+    @Resource(name = "aiChatExecutor")
+    private Executor aiChatExecutor;
+
+    /**
+     * 发起一轮 AI 会话,SSE 流式返回。
+     * 用户身份:主线程取 X-USER-BASE64 原始头,透传给下游工具 Feign 调 ratingApp(由其解析隔离)。
+     */
+    @PostMapping("/chat/stream")
+    public SseEmitter chatStream(@RequestBody AiChatRequest req,
+                                 @RequestHeader(value = "X-USER-BASE64", required = false) String userHeader) {
+        final String sessionId = (req.getSessionId() == null || req.getSessionId().isEmpty())
+            ? UUID.randomUUID().toString().replace("-", "") : req.getSessionId();
+        final String message = req.getMessage();
+        final String header = userHeader;
+
+        SseEmitter emitter = new SseEmitter(600000L); // 10 分钟超时
+        aiChatExecutor.execute(() -> {
+            try {
+                aiChatService.chat(header, sessionId, message, delta -> {
+                    try {
+                        JSONObject d = new JSONObject();
+                        d.put("text", delta);
+                        emitter.send(SseEmitter.event().name("delta").data(d.toJSONString()));
+                    } catch (Exception se) {
+                        throw new RuntimeException(se);
+                    }
+                });
+                JSONObject done = new JSONObject();
+                done.put("sessionId", sessionId);
+                done.put("finishReason", "end_turn");
+                emitter.send(SseEmitter.event().name("done").data(done.toJSONString()));
+                emitter.complete();
+            } catch (Exception e) {
+                log.warn("AI 会话处理失败 sessionId={}", sessionId, e);
+                try {
+                    JSONObject err = new JSONObject();
+                    err.put("code", -1);
+                    err.put("msg", "服务繁忙,请稍后再试");
+                    emitter.send(SseEmitter.event().name("error").data(err.toJSONString()));
+                    emitter.complete();
+                } catch (Exception ignore) {
+                    emitter.completeWithError(e);
+                }
+            }
+        });
+        return emitter;
+    }
+}

+ 157 - 0
mango-application/src/main/java/com/mangoo/rating/recommend/aspectj/ApiLogAspect.java

@@ -0,0 +1,157 @@
+package com.mangoo.rating.recommend.aspectj;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.support.spring.PropertyPreFilters;
+import com.mangoo.rating.recommend.annotation.ApiLog;
+import com.mangoo.rating.recommend.config.thread.ThreadPoolConfig;
+import com.mangoo.rating.recommend.dto.UserInfo;
+import com.mangoo.rating.recommend.enums.StatusEnum;
+import com.mangoo.rating.recommend.json.JSON;
+import com.mangoo.rating.recommend.po.SysOperLogPO;
+import com.mangoo.rating.recommend.utils.ServletUtils;
+import com.mangoo.rating.recommend.utils.ShiroUtils;
+import com.mangoo.rating.recommend.utils.StringUtils;
+import com.mangoo.rating.recommend.utils.UserUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.lang.reflect.Method;
+import java.time.LocalDateTime;
+import java.util.Map;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 操作日志记录处理
+ */
+@Slf4j
+@Aspect
+@Component
+public class ApiLogAspect {
+
+
+    @Resource
+    @Qualifier(ThreadPoolConfig.GOODS_THREAD_BEAN_NAME)
+    private ThreadPoolExecutor commonExecutorService;
+
+    /**
+     * 排除敏感属性字段
+     */
+    public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"};
+
+    @Pointcut("@annotation(com.mangoo.rating.recommend.annotation.ApiLog)")
+    public void logPointCut() {
+    }
+
+    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
+    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
+        handleLog(joinPoint, null, jsonResult);
+    }
+
+    @AfterThrowing(pointcut = "logPointCut()", throwing = "e")
+    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
+        handleLog(joinPoint, e, null);
+    }
+
+    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
+        try {
+            ApiLog controllerLog = getAnnotationLog(joinPoint);
+            if (controllerLog == null) {
+                return;
+            }
+
+            UserInfo user = UserUtils.getSimpleUserInfo();
+            SysOperLogPO operLog = new SysOperLogPO();
+            operLog.setStatus(StatusEnum.ENABLE.getCode());
+            operLog.setOperIp(ShiroUtils.getIp());
+            operLog.setJsonResult(StringUtils.substring(JSON.marshal(jsonResult), 0, 2000));
+            operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
+
+            if (user != null) {
+                operLog.setAdminId(user.getId());
+                operLog.setAdminAccount(user.getNickname());
+            }
+
+            setRequestValue(joinPoint, operLog);
+
+            String className = joinPoint.getTarget().getClass().getName();
+            String methodName = joinPoint.getSignature().getName();
+            operLog.setMethod(className + "." + methodName + "()");
+            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
+            operLog.setOperTime(LocalDateTime.now());
+
+            getControllerMethodDescription(controllerLog, operLog);
+
+            if (e != null) {
+                operLog.setStatus(StatusEnum.DISABLED.getCode());
+                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
+            }
+
+//            CompletableFuture.runAsync(() -> {
+//                sysOperLogManager.recordOperLog(operLog);
+//            }, commonExecutorService);
+
+            log.info("<<<< 接口请求参数 >>>>>>>:{}", operLog);
+        } catch (Exception exp) {
+            log.error("异常信息:{}", exp);
+        }
+    }
+
+    /**
+     * 获取注解中的方法描述信息
+     */
+    public void getControllerMethodDescription(ApiLog log, SysOperLogPO operLog) {
+        operLog.setBusinessType(log.businessType().ordinal());
+        operLog.setTitle(log.title());
+    }
+
+    /**
+     * 获取请求的参数,放到log中
+     */
+    private void setRequestValue(JoinPoint joinPoint, SysOperLogPO operLog) {
+        Map<String, String[]> map = ServletUtils.getRequest().getParameterMap();
+        StringBuilder inData = new StringBuilder();
+        if (StringUtils.isNotEmpty(map)) {
+            PropertyPreFilters.MySimplePropertyPreFilter excludeFilter = new PropertyPreFilters().addFilter();
+            excludeFilter.addExcludes(EXCLUDE_PROPERTIES);
+            inData.append(JSONObject.toJSONString(map, excludeFilter));
+        } else {
+            Object[] args = joinPoint.getArgs();
+            if (args != null && args.length > 0) {
+                for (Object arg : args) {
+                    try {
+                        if (!"java.lang.String".equals(arg.getClass().getName())) {
+                            inData.append(JSONObject.toJSONString(arg));
+                        } else {
+                            inData.append(arg);
+                        }
+                    } catch (Exception ignore) {
+                        // ignore single arg serialize error
+                    }
+                }
+            }
+        }
+        operLog.setOperParam(StringUtils.substring(inData.toString(), 0, 2000));
+    }
+
+    /**
+     * 是否存在注解,如果存在就获取
+     */
+    private ApiLog getAnnotationLog(JoinPoint joinPoint) {
+        Signature signature = joinPoint.getSignature();
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+        if (method != null) {
+            return method.getAnnotation(ApiLog.class);
+        }
+        return null;
+    }
+}

+ 130 - 0
mango-application/src/main/java/com/mangoo/rating/recommend/aspectj/Global.java

@@ -0,0 +1,130 @@
+package com.mangoo.rating.recommend.aspectj;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 全局配置类
+ * 
+ * @author tzy
+ */
+@Component
+@ConfigurationProperties(prefix = "tzy")
+public class Global
+{
+    /** 项目名称 */
+    private static String name;
+
+    /** 版本 */
+    private static String version;
+
+    /** 版权年份 */
+    private static String copyrightYear;
+
+    /** 实例演示开关 */
+    private static boolean demoEnabled;
+
+    /** 上传路径 */
+    private static String profile;
+
+    /** 获取地址开关 */
+    private static boolean addressEnabled;
+
+    public static String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        Global.name = name;
+    }
+
+    public static String getVersion()
+    {
+        return version;
+    }
+
+    public void setVersion(String version)
+    {
+        Global.version = version;
+    }
+
+    public static String getCopyrightYear()
+    {
+        return copyrightYear;
+    }
+
+    public void setCopyrightYear(String copyrightYear)
+    {
+        Global.copyrightYear = copyrightYear;
+    }
+
+    public static boolean isDemoEnabled()
+    {
+        return demoEnabled;
+    }
+
+    public void setDemoEnabled(boolean demoEnabled)
+    {
+        Global.demoEnabled = demoEnabled;
+    }
+
+    public static String getProfile()
+    {
+        return profile;
+    }
+
+    public void setProfile(String profile)
+    {
+        Global.profile = profile;
+    }
+
+    public static boolean isAddressEnabled()
+    {
+        return addressEnabled;
+    }
+
+    public void setAddressEnabled(boolean addressEnabled)
+    {
+        Global.addressEnabled = addressEnabled;
+    }
+
+    /**
+     * 获取图片上传路径
+     */
+    public static String getImageUploadPath()
+    {
+        return  "/tencheerApp/resource/profile";
+    }
+    
+    /**
+     * 获取小程序图片上传路径
+     */
+    public static String getWechatImageUploadPath()
+    {
+        return  "/tencheerApp/resource/wechat";
+    }
+    /**
+     * 获取头像上传路径
+     */
+    public static String getAvatarPath()
+    {
+        return getProfile() + "/avatar";
+    }
+    /**
+     * 获取下载路径
+     */
+    public static String getDownloadPath()
+    {
+        return getProfile() + "/download/";
+    }
+
+    /**
+     * 获取上传路径
+     */
+    public static String getUploadPath()
+    {
+        return getProfile() + "/upload";
+    }
+}

+ 23 - 0
mango-application/src/main/java/com/mangoo/rating/recommend/config/AiAsyncConfig.java

@@ -0,0 +1,23 @@
+package com.mangoo.rating.recommend.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+/** AI SSE 专用线程池:流式会话在独立线程执行,避免占用 Tomcat 请求线程 */
+@Configuration
+public class AiAsyncConfig {
+
+    @Bean("aiChatExecutor")
+    public Executor aiChatExecutor() {
+        ThreadPoolTaskExecutor e = new ThreadPoolTaskExecutor();
+        e.setCorePoolSize(4);
+        e.setMaxPoolSize(16);
+        e.setQueueCapacity(64);
+        e.setThreadNamePrefix("ai-chat-");
+        e.initialize();
+        return e;
+    }
+}

+ 131 - 0
mango-application/src/main/resources/application-dev.yml

@@ -0,0 +1,131 @@
+
+# 日志配置
+logging:
+  console.enabled: ${CONSOLE_ENABLED:true}
+  file.enabled: ${FILE_ENABLED:false}
+  level:
+    com.poyee: debug
+  fluentd:
+    enabled: ${FLUENTD_ENABLED:false}
+    host: ${FLUENTD_HOST:127.0.0.1}
+    port: ${FLUENTD_PORT:24225}
+  bee:
+    enabled: true
+
+# Spring配置
+spring:
+  application:
+    name: ratingRecommend
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driverClassName: org.postgresql.Driver
+    url: ${DB_URL:jdbc:postgresql://192.168.77.80:5432/mongo_grade}
+    username: ${DB_USERNAME:postgres}
+    password: ${DB_PASSWORD:123456}
+  redis:
+    host: ${REDIS_HOST:192.168.77.80}
+    port: ${REDIS_PORT:6379}
+    database: 0
+    password: ${REDIS_PASSWORD:Pass2025}
+    timeout: 60000  # 连接超时时长(毫秒)
+    # Lettuce连接池配置
+    lettuce:
+      pool:
+        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
+        max-wait: -1ms    # 连接池最大阻塞等待时间(使用负值表示没有限制)
+        max-idle: 10      # 连接池中的最大空闲连接
+        min-idle: 5       # 连接池中的最小空闲连接
+  #mq
+  rabbitmq:
+    host: ${SPRING_RABBITMQ_HOST:192.168.77.80}
+    port: ${SPRING_RABBITMQ_PORT:5672}
+    username: ${SPRING_RABBITMQ_USERNAME:order}
+    password: ${SPRING_RABBITMQ_PASSWORD:Pass2025}
+    #确认消息已发送到交换机(Exchange)
+    publisher-confirms: true
+    #确认消息已发送到队列(Queue)
+    publisher-returns: true
+    virtual-host: /order
+    template:
+      retry:
+        enabled: true
+      mandatory: true
+    listener:
+      simple:
+        acknowledge-mode: manual
+        #并发消费者初始化值
+        #concurrency: 10
+        #并发消费者的最大值
+        #max-concurrency: 20
+        #每个消费者每次监听时可拉取处理的消息数量
+        prefetch: 5
+      direct:
+        retry:
+          enabled: true
+          max-attempts: 1
+
+# MyBatis
+mybatis:
+  # 搜索指定包别名
+  typeAliasesPackage: com.poyee.**.domain
+  # 配置mapper的扫描,找到所有的mapper.xml映射文件
+  mapperLocations: classpath*:mapper/**/*Mapper.xml
+
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "*"  # 使用通配符启用所有端点
+      base-path: /actuator
+  endpoint:
+    health:
+      show-details: always
+      enabled: true
+    metrics:
+      enabled: true
+    prometheus:
+      enabled: true
+    info:
+      enabled: true
+  metrics:
+    export:
+      prometheus:
+        enabled: true
+        step: 30s
+        descriptions: true
+    enable:
+      jvm: true
+      system: true
+      logback: true
+      processor: true
+      http: true
+      hikaricp: true
+    tags:
+      application: order
+
+  health:
+    defaults.enabled: false
+    db.enabled: true
+    diskspace.enabled: true
+
+# knife4j 配置
+knife4j:
+  # 是否开启 knife4j
+  enabled: true
+  #生产模式屏蔽
+  production: true
+
+pay:
+  #微信支付
+  wxpay:
+    app:
+      appID: wx8ee47a255776f241
+
+
+
+# 物流api
+express:
+  resultUrl: https://m2-dev.hobbystocks.cn/expressInfo/result
+  flushTokenUrl: http://delivery/expressInfo/flushToken
+  flushOrderUrl: http://delivery/expressInfo/flushOrder
+

+ 136 - 0
mango-application/src/main/resources/application-prod.yml

@@ -0,0 +1,136 @@
+# 日志配置
+logging:
+  console.enabled: ${CONSOLE_ENABLED:true}
+  file.enabled: ${FILE_ENABLED:false}
+  level:
+    com.poyee: debug
+  fluentd:
+    enabled: ${FLUENTD_ENABLED:false}
+    host: ${FLUENTD_HOST:127.0.0.1}
+    port: ${FLUENTD_PORT:24225}
+  audit:
+    enabled: ${AUDIT_ENABLED:false}
+    host: ${AUDIT_HOST:192.168.1.51}
+    port: ${AUDIT_PORT:24225}
+  bee:
+    enabled: true
+
+# Spring配置
+spring:
+  application:
+    name: ratingRatingRecommend
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driverClassName: org.postgresql.Driver
+    url: ${DB_URL:jdbc:postgresql://192.168.77.80:5432/mongo_grade}
+    username: ${DB_USERNAME:postgres}
+    password: ${DB_PASSWORD:123456}
+  redis:
+    host: ${REDIS_HOST:192.168.77.80}
+    port: ${REDIS_PORT:6379}
+    database: 0
+    password: ${REDIS_PASSWORD:Pass2025}
+    timeout: 60000  # 连接超时时长(毫秒)
+    # Lettuce连接池配置
+    lettuce:
+      pool:
+        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
+        max-wait: -1ms    # 连接池最大阻塞等待时间(使用负值表示没有限制)
+        max-idle: 10      # 连接池中的最大空闲连接
+        min-idle: 5       # 连接池中的最小空闲连接
+  #mq
+  rabbitmq:
+    host: ${SPRING_RABBITMQ_HOST:192.168.77.80}
+    port: ${SPRING_RABBITMQ_PORT:5672}
+    username: ${SPRING_RABBITMQ_USERNAME:order}
+    password: ${SPRING_RABBITMQ_PASSWORD:Pass2025}
+    #确认消息已发送到交换机(Exchange)
+    publisher-confirms: true
+    #确认消息已发送到队列(Queue)
+    publisher-returns: true
+    virtual-host: /order
+    template:
+      retry:
+        enabled: true
+      mandatory: true
+    listener:
+      simple:
+        acknowledge-mode: manual
+        #并发消费者初始化值
+        #concurrency: 10
+        #并发消费者的最大值
+        #max-concurrency: 20
+        #每个消费者每次监听时可拉取处理的消息数量
+        prefetch: 5
+      direct:
+        retry:
+          enabled: true
+          max-attempts: 1
+
+# MyBatis
+mybatis:
+  # 搜索指定包别名
+  typeAliasesPackage: com.poyee.**.domain
+  # 配置mapper的扫描,找到所有的mapper.xml映射文件
+  mapperLocations: classpath*:mapper/**/*Mapper.xml
+
+#elasticsearch:
+#  rest:
+#    uris: ${ELASTIC_ENDPOINTS:192.168.1.62:9200,192.168.1.60:9200}
+
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "*"  # 使用通配符启用所有端点
+      base-path: /actuator
+  endpoint:
+    health:
+      show-details: always
+      enabled: true
+    metrics:
+      enabled: true
+    prometheus:
+      enabled: true
+    info:
+      enabled: true
+  metrics:
+    export:
+      prometheus:
+        enabled: true
+        step: 30s
+        descriptions: true
+    enable:
+      jvm: true
+      system: true
+      logback: true
+      processor: true
+      http: true
+      hikaricp: true
+    tags:
+      application: order
+
+  health:
+    defaults.enabled: false
+    db.enabled: true
+    diskspace.enabled: true
+
+# knife4j 配置
+knife4j:
+  # 是否开启 knife4j
+  enabled: true
+  #生产模式屏蔽
+  production: true
+
+pay:
+  #微信支付
+  wxpay:
+    app:
+      appID: wx8ee47a255776f241
+
+
+# 物流api
+express:
+  resultUrl: https://m2.hobbystocks.cn/expressInfo/result
+  flushTokenUrl: http://delivery/expressInfo/flushToken
+  flushOrderUrl: http://delivery/expressInfo/flushOrder

+ 347 - 0
mango-application/src/main/resources/application.yml

@@ -0,0 +1,347 @@
+
+# 开发环境配置
+server:
+  # 服务器的HTTP端口,默认为80
+  port: 8094
+  tomcat:
+    # tomcat的URI编码
+    uri-encoding: UTF-8
+    # tomcat最大线程数,默认为200
+    max-threads: 800
+    # Tomcat启动初始化的线程数,默认值25
+    min-spare-threads: 30
+    #超时时间
+    connection-timeout: 600000
+    accesslog:
+      enabled: true
+      buffered: true
+      prefix: access_log
+      file-date-format: .yyyy-MM-dd
+  mvc:
+    pathmatch:
+      matching-strategy: ant_path_matcher
+
+# 日志配置
+logging:
+  console.enabled: ${CONSOLE_ENABLED:true}
+  file.enabled: ${FILE_ENABLED:false}
+  level:
+    com.poyee: debug
+  fluentd:
+    enabled: ${FLUENTD_ENABLED:false}
+    host: ${FLUENTD_HOST:127.0.0.1}
+    port: ${FLUENTD_PORT:24225}
+  audit:
+    enabled: ${AUDIT_ENABLED:false}
+    host: ${AUDIT_HOST:192.168.1.51}
+    port: ${AUDIT_PORT:24225}
+  bee:
+    enabled: true
+
+# Spring配置
+spring:
+  application:
+    name: ratingRecommend
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driverClassName: org.postgresql.Driver
+#    url: jdbc:postgresql://192.168.50.8:5432/test01
+    url: jdbc:postgresql://192.168.77.80:5432/mongo_grade?useUnicode=true&characterEncoding=UTF-8
+    username: postgres
+    password: 123456
+    druid:
+      # 初始连接数
+      initialSize: 5
+      # 最小连接池数量
+      minIdle: 10
+      # 最大连接池数量
+      maxActive: 50
+      # 配置获取连接等待超时的时间
+      maxWait: 60000
+      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+      timeBetweenEvictionRunsMillis: 60000
+      # 配置一个连接在池中最小生存的时间,单位是毫秒
+      minEvictableIdleTimeMillis: 300000
+      # 配置一个连接在池中最大生存的时间,单位是毫秒
+      maxEvictableIdleTimeMillis: 900000
+      # 配置检测连接是否有效
+      validationQuery: SELECT 1
+      testWhileIdle: true
+      testOnBorrow: false
+      testOnReturn: false
+      webStatFilter:
+        enabled: true
+      statViewServlet:
+        enabled: true
+        # 设置白名单,不填则允许所有访问
+        allow:
+        url-pattern: /druid/*
+        # 控制台管理用户名和密码
+        login-username:
+        login-password:
+      filter:
+        stat:
+          enabled: true
+          # 慢SQL记录
+          log-slow-sql: true
+          slow-sql-millis: 500
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+  redis:
+#    database: 1
+#    host: 192.168.50.8
+#    port: 26379
+#    sentinel:
+#      master: ${SENTINEL_MASTER:poyee-master}
+#      nodes: ${SENTINEL_NODES:192.168.50.8:26379}
+#    password: Pass2010    # 密码(默认为空)
+#    timeout: 60000  # 连接超时时长(毫秒)
+#    pool:
+#      max-active: 100  # 连接池最大连接数(使用负值表示没有限制)
+#      max-wait: -1ms    # 连接池最大阻塞等待时间(使用负值表示没有限制)
+#      max-idle: 50      # 连接池中的最大空闲连接
+#      min-idle: 20       # 连接池中的最小空闲连接
+    # 单机Redis配置
+    host: 192.168.77.80
+    port: 6379
+    # 如果你的Redis有密码,写你的密码,没有就注释掉这行
+    password: Pass2025
+    database: 0
+    timeout: 60000
+    # Lettuce连接池配置(解决空闲连接被Redis服务端断开导致Connection reset by peer)
+    lettuce:
+      pool:
+        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
+        max-wait: -1ms    # 连接池最大阻塞等待时间(使用负值表示没有限制)
+        max-idle: 10      # 连接池中的最大空闲连接
+        min-idle: 5       # 连接池中的最小空闲连接
+
+  #mq
+  rabbitmq:
+    host: 192.168.77.80
+    port: 5672
+    username: order
+    password: Pass2025
+    #确认消息已发送到交换机(Exchange)
+    publisher-confirms: true
+    #确认消息已发送到队列(Queue)
+    publisher-returns: true
+    virtual-host: /order
+    template:
+      retry:
+        enabled: true
+      mandatory: true
+    listener:
+      simple:
+        acknowledge-mode: manual
+        #并发消费者初始化值
+        #concurrency: 10
+        #并发消费者的最大值
+        #max-concurrency: 20
+        #每个消费者每次监听时可拉取处理的消息数量
+        prefetch: 5
+      direct:
+        retry:
+          enabled: true
+          max-attempts: 1
+  flyway:
+    # 是否启用flyway
+    enabled: true
+    # 指定表
+    table: flyway_schema_history_admin
+    # 禁止清理数据库表
+    clean-disabled: true
+    # 编码格式,默认UTF-8
+    encoding: UTF-8
+    # 迁移sql脚本文件存放路径,默认db/migration
+    locations: classpath:db/migration
+    # 迁移sql脚本文件名称的前缀,默认V
+    sql-migration-prefix: V
+    # 迁移sql脚本文件名称的分隔符,默认2个下划线__
+    sql-migration-separator: _
+    # 迁移sql脚本文件名称的后缀
+    sql-migration-suffixes: .sql
+    # 迁移时是否进行校验,默认true
+#    validate-on-migrate: true
+    # 当迁移发现数据库非空且存在没有元数据的表时,自动执行基准迁移,新建schema_version表
+    baseline-on-migrate: true
+    url: ${DB_URL:jdbc:postgresql://192.168.77.80:5432/mongo_grade}
+    user: ${DB_USERNAME:postgres}
+    password: ${DB_PASSWORD:123456}
+    # 禁用描述校验(避免mismatch报错)
+    validate-migration-naming: false
+    # 或禁用整体校验(不推荐,会跳过版本/描述等所有校验)
+    validate-on-migrate: false
+# MyBatis
+mybatis:
+  # 搜索指定包别名
+  typeAliasesPackage: com.poyee.common.po,com.poyee.**.domain
+ # 配置mapper的扫描,找到所有的mapper.xml映射文件
+  mapperLocations: classpath*:mapper/**/*Mapper.xml
+
+
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "*"  # 使用通配符启用所有端点
+      base-path: /actuator
+  endpoint:
+    health:
+      show-details: always
+      enabled: true
+    metrics:
+      enabled: true
+    prometheus:
+      enabled: true
+    info:
+      enabled: true
+  metrics:
+    export:
+      prometheus:
+        enabled: true
+        step: 30s
+        descriptions: true
+    enable:
+      jvm: true
+      system: true
+      logback: true
+      processor: true
+      http: true
+      hikaricp: true
+    tags:
+      application: order
+
+  health:
+    defaults.enabled: false
+    db.enabled: true
+    diskspace.enabled: true
+
+# knife4j 配置
+knife4j:
+  # 是否开启 knife4j
+  enabled: true
+  #生产模式屏蔽
+  production: true
+  basic: #是否需要登录
+    enable: true
+    username: admin
+    password: 123456
+
+pay:
+  #微信支付
+  wxpay:
+    app:
+      appID: wx8ee47a255776f241
+
+# 腾讯云COS配置
+tencent:
+  cos:
+    secret-id: ${TENCENT_COS_SECRET_ID:AKIDeCb2X834B3yz4EUdpzyoQoCRWUcm1JgT}
+    secret-key: ${TENCENT_COS_SECRET_KEY:dXUSPLGHLSd58C6nGUTJySiyFUgrz236}
+    region: ${TENCENT_COS_REGION:ap-shanghai}
+    bucket-name: ${TENCENT_COS_BUCKET_NAME:ranking-1309648802}
+    domain: ${TENCENT_COS_DOMAIN:https://your-domain.com}
+    prefix: ${TENCENT_COS_PREFIX:/mongoo/rating/images/}
+    max-file-size: ${TENCENT_COS_MAX_FILE_SIZE:10485760}
+    allow-file-types: ${TENCENT_COS_ALLOW_FILE_TYPES:jpg,jpeg,png,gif,bmp,webp}
+
+
+generate-server-url:
+      report: http://192.168.77.249:7755/api/rating/generate
+      report-status: http://192.168.77.249:7755/api/cards/review_state
+      score: http://192.168.77.249:7755/api/cards/card_list_filter
+      batch-score: http://192.168.77.249:7755/api/cards/batch_query/id_score
+
+camera-server-url:
+      start-task: http://192.168.31.191:7961/api/monitor/start_task
+      stop-task: http://192.168.31.191:7961/api/monitor/stop_task
+      query-task: http://192.168.31.191:7961/api/monitor/query_task
+
+# Feign客户端服务URL配置
+camera-service:
+  url: http://192.168.31.191:7961
+
+generate-service:
+  url: http://grade-service
+
+pay-service:
+  url: http://192.168.77.249:7756
+
+coupon-service:
+  url: http://coupon
+
+order-service:
+  url: http://order
+
+
+# Feign配置
+feign:
+  client:
+    config:
+      default:
+        connectTimeout: 60000
+        readTimeout: 60000
+        loggerLevel: full
+
+
+# 物流api
+express:
+  resultUrl: https://m2-dev.hobbystocks.cn/expressInfo/result
+  flushTokenUrl: http://delivery/expressInfo/flushToken
+  flushOrderUrl: http://delivery/expressInfo/flushOrder
+
+
+
+# Shiro
+shiro:
+  user:
+    # 登录地址
+    loginUrl: /login
+    # 权限认证失败地址
+    unauthorizedUrl: /unauth
+    # 首页地址
+    indexUrl: /index
+    # 验证码开关
+    captchaEnabled: false
+    # 验证码类型 math 数组计算 char 字符
+    captchaType: math
+  cookie:
+    # 设置Cookie的域名 默认空,即当前访问的域名
+    domain:
+    # 设置cookie的有效访问路径
+    path: /
+    # 设置HttpOnly属性
+    httpOnly: true
+    # 设置Cookie的过期时间,天为单位
+    maxAge: 30
+    # 设置密钥,务必保持唯一性(生成方式,直接拷贝到main运行即可)KeyGenerator keygen = KeyGenerator.getInstance("AES"); SecretKey deskey = keygen.generateKey(); System.out.println(Base64.encodeToString(deskey.getEncoded()));
+    cipherKey: zSyK5Kp6PZAAjlT+eeNMlg==
+  session:
+    # Session超时时间,-1代表永不过期(默认30分钟)
+    expireTime: 480
+    # 同步session到数据库的周期(默认1分钟)
+    dbSyncPeriod: 1
+    # 相隔多久检查一次session的有效性,默认就是10分钟
+    validationInterval: 10
+    # 同一个用户最大会话数,比如2的意思是同一个账号允许最多同时两个人登录(默认-1不限制)
+    maxSession: 10
+    # 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户
+    kickoutAfter: false
+
+# AI 智能助手
+ai:
+  base-url: ${AI_GATEWAY_BASE_URL:https://sub2api.hobbystocks.cn}
+  api-key: ${AI_GATEWAY_API_KEY:replace-me}
+  model: ${AI_MODEL:claude-opus-4-8}
+  max-tokens: 4096
+  session-ttl-minutes: 30
+  max-iterations: 5
+  connect-timeout-ms: 10000
+  read-timeout-ms: 120000
+
+# ratingApp 业务服务地址(AI 助手通过 Feign 远程调其订单/物流接口)
+rating-app:
+  url: ${RATING_APP_URL:192.168.31.111:8091}

+ 751 - 0
mango-application/src/main/resources/db/migration/V1.1_mongo_order_create.sql

@@ -0,0 +1,751 @@
+-- 订单管理表结构
+-- 1. 订单主表(t_rating_order)
+CREATE TABLE "public"."t_rating_order" (
+                                           id BIGSERIAL PRIMARY KEY,
+                                           "order_no" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
+                                           "user_id" int8 NOT NULL,
+                                           "total_amount" numeric(10,2) NOT NULL,
+                                           "discount_amount" numeric(10,2) DEFAULT 0.00,
+                                           "pay_amount" numeric(10,2) NOT NULL,
+                                           "coupon_id" int8,
+                                           "order_status" int4 NOT NULL,
+                                           "evaluate_type" int4 NOT NULL,
+                                           "ship_type" varchar(32) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                           "logistics_company" varchar(32) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                           "logistics_no" varchar(64) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                           "return_address_id" int8 NOT NULL,
+                                           "remark" text COLLATE "pg_catalog"."default",
+                                           "extend_fields" text COLLATE "pg_catalog"."default",
+                                           "version" int4 DEFAULT 1,
+                                           "order_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                           "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                           "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                           "create_user" int8 NOT NULL,
+                                           "update_user" int8 NOT NULL,
+                                           "del_flag" int2 DEFAULT 0,
+                                           "card_style" int4,
+                                           "evaluate_efficiency" int4,
+                                           "in_status" int4,
+                                           "in_time" timestamp(6),
+                                           "card_quantity" int4 DEFAULT 0,
+                                           "send_back_ship_time" timestamp(6) DEFAULT NULL::timestamp without time zone,
+                                           "platform_send_back_no" varchar(64) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                           CONSTRAINT "uk_rating_order_no" UNIQUE ("order_no")
+)
+;
+
+ALTER TABLE "public"."t_rating_order"
+    OWNER TO "postgres";
+
+CREATE INDEX "idx_rating_order_create_time" ON "public"."t_rating_order" USING btree (
+    "create_time" "pg_catalog"."timestamp_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_rating_order_evaluate_type" ON "public"."t_rating_order" USING btree (
+    "evaluate_type" "pg_catalog"."int4_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_rating_order_status" ON "public"."t_rating_order" USING btree (
+    "order_status" "pg_catalog"."int4_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_rating_order_user_id" ON "public"."t_rating_order" USING btree (
+    "user_id" "pg_catalog"."int8_ops" ASC NULLS LAST
+    );
+
+COMMENT ON COLUMN "public"."t_rating_order"."id" IS '主键ID';
+
+COMMENT ON COLUMN "public"."t_rating_order"."order_no" IS '订单编号(编号生成规则待定)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."user_id" IS '用户ID(关联t_user.id)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."total_amount" IS '订单总金额';
+
+COMMENT ON COLUMN "public"."t_rating_order"."discount_amount" IS '优惠金额';
+
+COMMENT ON COLUMN "public"."t_rating_order"."pay_amount" IS '实付金额';
+
+COMMENT ON COLUMN "public"."t_rating_order"."coupon_id" IS '使用的优惠券ID(关联t_coupon.id)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."order_status" IS '订单状态 (100 - 待支付,101 - 已支付待寄出,102 - 已支付已寄出,201 - 订单超时,202 - 退款中,203 - 用户取消,204 - 退款完成,205 - 退款失败,301 - 预评 - 待预审,302 - 预评 - 预审通过,303 - 预评 - 预审未通过,304 - 送评 - 待送评,305 - 送评 - 待送评未支付,306 - 送评 - 待送评已支付,307 - 送评 - 送评中,401 - 评级中 - 拍照,402 - 评级中 - 打分,403 - 评级中 - 生成报告,404 - 评级中 - 审核报告,405 - 评级中 - 审核报告通过,406 - 评级中 - 审核报告未通过,501 - 打印标签,502 - 封装,503 - 打包发货,504 - 已发货,505 - 订单完成)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."evaluate_type" IS '评级类型(枚举:1-自评级-TCG,2-自评级-体育卡牌,3-PSA-体育卡牌)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."ship_type" IS '寄出方式(枚举:SF_VISIT-顺丰上门,SELF_SEND-自己寄出)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."logistics_company" IS '快递公司(如SF-顺丰)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."logistics_no" IS '物流单号';
+
+COMMENT ON COLUMN "public"."t_rating_order"."return_address_id" IS '寄回地址ID(关联t_user_address.id)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."remark" IS '订单备注';
+
+COMMENT ON COLUMN "public"."t_rating_order"."extend_fields" IS '扩展字段(动态存储如保价金额、预估重量等)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."version" IS '乐观锁版本号';
+
+COMMENT ON COLUMN "public"."t_rating_order"."order_time" IS '下单时间';
+
+COMMENT ON COLUMN "public"."t_rating_order"."create_time" IS '创建时间';
+
+COMMENT ON COLUMN "public"."t_rating_order"."update_time" IS '更新时间';
+
+COMMENT ON COLUMN "public"."t_rating_order"."create_user" IS '创建人(用户ID)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."update_user" IS '更新人(用户ID)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."del_flag" IS '逻辑删除(0=正常,1=删除)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."card_style" IS '评级款式(枚举:1-普通款,2-设计款,3-高端款)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."evaluate_efficiency" IS '评级时效(枚举:1-普通,2-快速,3-闪评)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."in_status" IS '入库状态(枚举:1-未入库,2-已签收,3-已入库)';
+
+COMMENT ON COLUMN "public"."t_rating_order"."in_time" IS '入库时间';
+
+COMMENT ON COLUMN "public"."t_rating_order"."card_quantity" IS '卡片数量';
+
+COMMENT ON COLUMN "public"."t_rating_order"."send_back_ship_time" IS '平台寄回发货时间';
+
+COMMENT ON COLUMN "public"."t_rating_order"."platform_send_back_no" IS '平台寄回单号';
+
+COMMENT ON TABLE "public"."t_rating_order" IS '订单主表 ';
+
+
+-- 2. 订单子卡表(t_rating_order_card)
+CREATE TABLE "public"."t_rating_order_card" (
+                                                id BIGSERIAL PRIMARY KEY,
+                                                "order_no" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
+                                                "card_no" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
+                                                "card_status" int2,
+                                                "card_style" int8,
+                                                "evaluate_efficiency" int2,
+                                                "score_threshold" numeric(3,1) DEFAULT NULL::numeric,
+                                                "is_original_return" bool DEFAULT false,
+                                                "front_image_url" varchar(255) COLLATE "pg_catalog"."default",
+                                                "back_image_url" varchar(255) COLLATE "pg_catalog"."default",
+                                                "recognized_info" text COLLATE "pg_catalog"."default",
+                                                "evaluate_report" text COLLATE "pg_catalog"."default",
+                                                "evaluate_no" varchar(64) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                                "finished_image_url" varchar(255) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                                "version" int4 DEFAULT 1,
+                                                "logistics_company" varchar(32) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                                "logistics_no" varchar(64) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                                "last_time_ship" timestamp(6) DEFAULT CURRENT_TIMESTAMP,
+                                                "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                "del_flag" int2 NOT NULL DEFAULT 0,
+                                                "user_id" int8,
+                                                "scheme_no" varchar(64) COLLATE "pg_catalog"."default",
+                                                CONSTRAINT "uk_rating_card_no" UNIQUE ("card_no")
+)
+;
+
+ALTER TABLE "public"."t_rating_order_card"
+    OWNER TO "postgres";
+
+CREATE INDEX "idx_rating_card_evaluate_no" ON "public"."t_rating_order_card" USING btree (
+    "evaluate_no" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_rating_card_order_id" ON "public"."t_rating_order_card" USING btree (
+    "order_no" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_rating_card_status" ON "public"."t_rating_order_card" USING btree (
+    "card_status" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
+    );
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."id" IS '主键ID';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."order_no" IS '订单编号(关联t_rating_order.order_no)';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."card_no" IS '卡片编号(唯一,如CARD20260107001001)';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."card_status" IS '卡片状态(枚举:100=未开始,101=待寄出,102=已寄出,202=已入库,203=预审未通过,204=预审通过,205=待送评,301=评级中-拍照,302=评级中-打分,303=评级中-生成报告,304=评级中-审核报告,305=评级中-审核报告通过,306=评级中-审核报告未通过,401=打印标签,402=封装评级壳,501=已发货-已寄出,502=完成)';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."card_style" IS '评级款式Id';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."evaluate_efficiency" IS '评级时效(枚举:1-普通,2-快速,3-闪评)';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."score_threshold" IS '评级壳分数阈值(如8.5)';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."is_original_return" IS '是否原卡寄回';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."front_image_url" IS '卡片正面图片URL';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."back_image_url" IS '卡片背面图片URL';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."recognized_info" IS '识别信息(球员/卡种/编号等,如{"player":"库里","series":"2024篮球","number":"100"})';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."evaluate_report" IS '评级报告(评分/子分项/评语等,如{"score":9.0,"subItems":{"centering":9.5},"comment":"品相极佳"})';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."evaluate_no" IS '评级编号(机台/PSA返回的唯一编号)';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."finished_image_url" IS '成品图片URL';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."version" IS '乐观锁版本号';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."logistics_company" IS '物流公司';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."logistics_no" IS '物流单号';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."last_time_ship" IS '最晚发货时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."create_time" IS '创建时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."update_time" IS '更新时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."del_flag" IS '逻辑删除(0=正常,1=删除)';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."user_id" IS '用户id';
+
+COMMENT ON COLUMN "public"."t_rating_order_card"."scheme_no" IS '方案编号/名称';
+
+COMMENT ON TABLE "public"."t_rating_order_card" IS '订单子卡表';
+
+
+-- 3. 订单支付表(t_rating_order_pay)
+
+CREATE TABLE "public"."t_rating_order_pay" (
+                                               id BIGSERIAL PRIMARY KEY,
+                                               "order_id" int8 NOT NULL,
+                                               "pay_no" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
+                                               "pay_amount" numeric(10,2) NOT NULL,
+                                               "pay_type" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
+                                               "pay_status" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
+                                               "pay_time" timestamp(6),
+                                               "trade_no" varchar(64) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                               "extend_fields" text COLLATE "pg_catalog"."default" DEFAULT '{}'::jsonb,
+                                               "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                               "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                               "del_flag" int2 DEFAULT 0,
+                                               CONSTRAINT "uk_rating_pay_no" UNIQUE ("pay_no")
+)
+;
+
+ALTER TABLE "public"."t_rating_order_pay"
+    OWNER TO "postgres";
+
+CREATE INDEX "idx_rating_pay_order_id" ON "public"."t_rating_order_pay" USING btree (
+    "order_id" "pg_catalog"."int8_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_rating_pay_status" ON "public"."t_rating_order_pay" USING btree (
+    "pay_status" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
+    );
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."id" IS '主键ID';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."order_id" IS '订单ID(关联t_rating_order.id)';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."pay_no" IS '支付单号(唯一)';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."pay_amount" IS '支付金额';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."pay_type" IS '支付方式(枚举:WECHAT_PAY-微信支付)';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."pay_status" IS '支付状态(枚举:PENDING-待支付,SUCCESS-支付成功,FAILED-支付失败,TIMEOUT-支付超时)';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."pay_time" IS '支付时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."trade_no" IS '第三方支付流水号';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."extend_fields" IS '扩展字段(支付额外信息)';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."create_time" IS '创建时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."update_time" IS '更新时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_pay"."del_flag" IS '逻辑删除(0=正常,1=删除)';
+
+COMMENT ON TABLE "public"."t_rating_order_pay" IS '订单支付表 ';
+
+-- 4. 订单退款表(t_rating_order_refund)
+
+CREATE TABLE "public"."t_rating_order_refund" (
+                                                  id BIGSERIAL PRIMARY KEY,
+                                                  "refund_no" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
+                                                  "order_id" int8 NOT NULL,
+                                                  "card_id" int8,
+                                                  "refund_amount" numeric(10,2) NOT NULL,
+                                                  "refund_type" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
+                                                  "refund_status" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
+                                                  "refund_reason" varchar(255) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                                  "fail_reason" varchar(255) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                                  "refund_time" timestamp(6),
+                                                  "trade_no" varchar(64) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                                  "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                  "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                  "del_flag" int2 DEFAULT 0,
+                                                  CONSTRAINT "uk_rating_refund_no" UNIQUE ("refund_no")
+)
+;
+
+ALTER TABLE "public"."t_rating_order_refund"
+    OWNER TO "postgres";
+
+CREATE INDEX "idx_rating_refund_order_id" ON "public"."t_rating_order_refund" USING btree (
+    "order_id" "pg_catalog"."int8_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_rating_refund_status" ON "public"."t_rating_order_refund" USING btree (
+    "refund_status" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
+    );
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."id" IS '主键ID';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."refund_no" IS '退款单号(唯一)';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."order_id" IS '订单ID(关联t_rating_order.id)';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."card_id" IS '子卡ID(单卡退款时关联t_rating_order_card.id,整单退款为NULL)';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."refund_amount" IS '退款金额';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."refund_type" IS '退款类型(枚举:FULL_REFUND-整单退款,PART_REFUND-部分退款,PRE_REVIEW_FAILED_REFUND-预审未通过退款)';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."refund_status" IS '退款状态(枚举:REFUNDING-退款中,SUCCESS-退款成功,FAILED-退款失败)';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."refund_reason" IS '退款原因';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."fail_reason" IS '退款失败原因';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."refund_time" IS '退款完成时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."trade_no" IS '第三方退款流水号';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."create_time" IS '创建时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."update_time" IS '更新时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_refund"."del_flag" IS '逻辑删除(0=正常,1=删除)';
+
+COMMENT ON TABLE "public"."t_rating_order_refund" IS '订单退款表';
+
+
+-- 5. 订单操作日志表(t_rating_order_operation_log)
+
+CREATE TABLE "public"."t_rating_order_operation_log" (
+                                                         id BIGSERIAL PRIMARY KEY,                                                         "order_id" int8 NOT NULL,
+                                                         "card_id" int8,
+                                                         "operator_id" int8 NOT NULL,
+                                                         "operator_type" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
+                                                         "operation_type" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
+                                                         "before_status" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
+                                                         "after_status" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
+                                                         "operation_desc" text COLLATE "pg_catalog"."default",
+                                                         "operation_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                         "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                         "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                         "del_flag" int2 DEFAULT 0
+)
+;
+
+ALTER TABLE "public"."t_rating_order_operation_log"
+    OWNER TO "postgres";
+
+CREATE INDEX "idx_rating_operation_order_id" ON "public"."t_rating_order_operation_log" USING btree (
+    "order_id" "pg_catalog"."int8_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_rating_operation_time" ON "public"."t_rating_order_operation_log" USING btree (
+    "operation_time" "pg_catalog"."timestamp_ops" ASC NULLS LAST
+    );
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."id" IS '主键ID';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."order_id" IS '订单ID(关联t_rating_order.id)';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."card_id" IS '子卡ID(关联t_rating_order_card.id)';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."operator_id" IS '操作人ID(用户ID/管理员ID)';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."operator_type" IS '操作人类型(枚举:USER-用户,ADMIN-管理员,SYSTEM-系统)';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."operation_type" IS '操作类型(枚举:CREATE_ORDER-创建订单,PAY-支付,UPDATE_LOGISTICS-更新物流,PRE_REVIEW-预审,EVALUATE-评级,REFUND-退款,SHIP-发货,CONFIRM_RECEIPT-确认收货)';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."before_status" IS '操作前状态';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."after_status" IS '操作后状态';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."operation_desc" IS '操作描述';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."operation_time" IS '操作时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."create_time" IS '创建时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."update_time" IS '更新时间';
+
+COMMENT ON COLUMN "public"."t_rating_order_operation_log"."del_flag" IS '逻辑删除(0=正常,1=删除)';
+
+COMMENT ON TABLE "public"."t_rating_order_operation_log" IS '订单操作日志表';
+
+
+-- 6. 球员卡密信息表(t_recognized_info)
+
+CREATE TABLE "public"."t_recognized_info" (
+                                              id BIGSERIAL PRIMARY KEY,
+                                              "card_id" int8,
+                                              "order_no" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
+                                              "player" varchar(128) COLLATE "pg_catalog"."default",
+                                              "year" varchar(32) COLLATE "pg_catalog"."default",
+                                              "series" varchar(128) COLLATE "pg_catalog"."default",
+                                              "card_set" varchar(128) COLLATE "pg_catalog"."default",
+                                              "confidential_id" varchar(64) COLLATE "pg_catalog"."default",
+                                              "limit_id" varchar(64) COLLATE "pg_catalog"."default",
+                                              "create_user" varchar(64) COLLATE "pg_catalog"."default",
+                                              "update_user" varchar(64) COLLATE "pg_catalog"."default",
+                                              "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                              "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                              "del_flag" int2 NOT NULL DEFAULT 0
+)
+;
+
+ALTER TABLE "public"."t_recognized_info"
+    OWNER TO "postgres";
+
+CREATE INDEX "idx_t_recognized_info_card_id" ON "public"."t_recognized_info" USING btree (
+    "card_id" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_t_recognized_info_del_flag" ON "public"."t_recognized_info" USING btree (
+    "del_flag" "pg_catalog"."int2_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_t_recognized_info_order_no" ON "public"."t_recognized_info" USING btree (
+    "order_no" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
+    );
+
+COMMENT ON COLUMN "public"."t_recognized_info"."id" IS '主键ID';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."card_id" IS '卡ID';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."order_no" IS '订单编号';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."player" IS '球员';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."year" IS '年份';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."series" IS '系列';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."card_set" IS '卡种';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."confidential_id" IS '卡密编号';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."limit_id" IS '限编';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."create_user" IS '创建人';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."update_user" IS '修改人';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."create_time" IS '创建时间';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."update_time" IS '修改时间';
+
+COMMENT ON COLUMN "public"."t_recognized_info"."del_flag" IS '删除标识(0-未删,1-已删)';
+
+COMMENT ON TABLE "public"."t_recognized_info" IS '球员卡密信息表';
+
+
+
+-- 7. 用户寄回地址表(t_user_address)
+
+CREATE TABLE "public"."t_user_address" (
+                                           id BIGSERIAL PRIMARY KEY,                                           "user_id" int8,
+                                           "receiver_name" varchar(32) COLLATE "pg_catalog"."default",
+                                           "receiver_phone" varchar(16) COLLATE "pg_catalog"."default",
+                                           "province" varchar(32) COLLATE "pg_catalog"."default",
+                                           "city" varchar(32) COLLATE "pg_catalog"."default",
+                                           "district" varchar(32) COLLATE "pg_catalog"."default",
+                                           "detail_address" varchar(255) COLLATE "pg_catalog"."default",
+                                           "postal_code" varchar(8) COLLATE "pg_catalog"."default" DEFAULT ''::character varying,
+                                           "is_default" int2 DEFAULT 0,
+                                           "address_type" varchar(16) COLLATE "pg_catalog"."default" DEFAULT 'NORMAL'::character varying,
+                                           "remark" varchar(128) COLLATE "pg_catalog"."default" DEFAULT ''::character varying,
+                                           "create_time" timestamp(6) DEFAULT CURRENT_TIMESTAMP,
+                                           "update_time" timestamp(6) DEFAULT CURRENT_TIMESTAMP,
+                                           "del_flag" int2 DEFAULT 0
+)
+;
+
+ALTER TABLE "public"."t_user_address"
+    OWNER TO "postgres";
+
+COMMENT ON COLUMN "public"."t_user_address"."id" IS '主键ID 自增';
+
+COMMENT ON COLUMN "public"."t_user_address"."user_id" IS '用户ID,关联用户表t_user.id';
+
+COMMENT ON COLUMN "public"."t_user_address"."receiver_name" IS '收件人姓名';
+
+COMMENT ON COLUMN "public"."t_user_address"."receiver_phone" IS '收件人手机号';
+
+COMMENT ON COLUMN "public"."t_user_address"."province" IS '省份';
+
+COMMENT ON COLUMN "public"."t_user_address"."city" IS '城市';
+
+COMMENT ON COLUMN "public"."t_user_address"."district" IS '区县/行政区';
+
+COMMENT ON COLUMN "public"."t_user_address"."detail_address" IS '详细地址(街道/小区/门牌号)';
+
+COMMENT ON COLUMN "public"."t_user_address"."postal_code" IS '邮政编码(选填)';
+
+COMMENT ON COLUMN "public"."t_user_address"."is_default" IS '是否默认地址 0-否 1-是,用户仅能有1个默认地址';
+
+COMMENT ON COLUMN "public"."t_user_address"."address_type" IS '地址类型 NORMAL-普通地址  COLLECT-代收点';
+
+COMMENT ON COLUMN "public"."t_user_address"."remark" IS '地址备注(例:丰巢柜2号、门卫室代收)';
+
+COMMENT ON COLUMN "public"."t_user_address"."create_time" IS '创建时间';
+
+COMMENT ON COLUMN "public"."t_user_address"."update_time" IS '更新时间';
+
+COMMENT ON COLUMN "public"."t_user_address"."del_flag" IS '逻辑删除 0-正常 1-删除';
+
+COMMENT ON TABLE "public"."t_user_address" IS '用户寄回地址表';
+
+
+-- 8. 标签打印记录表(t_print_label_record)
+
+CREATE TABLE "public"."t_print_label_record" (
+                                                 id BIGSERIAL PRIMARY KEY,                                                 "label_title" varchar(50) COLLATE "pg_catalog"."default" NOT NULL,
+                                                 "label_content" text COLLATE "pg_catalog"."default" NOT NULL,
+                                                 "print_ip" varchar(30) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                                 "print_user" varchar(30) COLLATE "pg_catalog"."default" NOT NULL,
+                                                 "print_status" int4 DEFAULT 1,
+                                                 "print_msg" varchar(255) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                                 "printer_name" varchar(100) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying,
+                                                 "create_time" timestamp(6) DEFAULT CURRENT_TIMESTAMP,
+                                                 "del_flag" int4 DEFAULT 0
+)
+;
+
+ALTER TABLE "public"."t_print_label_record"
+    OWNER TO "postgres";
+
+COMMENT ON COLUMN "public"."t_print_label_record"."id" IS '主键ID';
+
+COMMENT ON COLUMN "public"."t_print_label_record"."label_title" IS '标签标题';
+
+COMMENT ON COLUMN "public"."t_print_label_record"."label_content" IS '标签打印内容';
+
+COMMENT ON COLUMN "public"."t_print_label_record"."print_ip" IS '打印操作IP';
+
+COMMENT ON COLUMN "public"."t_print_label_record"."print_user" IS '打印人账号';
+
+COMMENT ON COLUMN "public"."t_print_label_record"."print_status" IS '打印状态 1-成功 2-失败';
+
+COMMENT ON COLUMN "public"."t_print_label_record"."print_msg" IS '打印结果信息';
+
+COMMENT ON COLUMN "public"."t_print_label_record"."printer_name" IS '使用的打印机名称';
+
+COMMENT ON COLUMN "public"."t_print_label_record"."create_time" IS '打印时间';
+
+COMMENT ON COLUMN "public"."t_print_label_record"."del_flag" IS '删除标记 0-未删 1-已删';
+
+COMMENT ON TABLE "public"."t_print_label_record" IS '标签打印记录表';
+
+
+
+-- 9. 卡片设计款式模板表(t_card_style_template)
+
+CREATE TABLE "public"."t_card_style_template" (
+                                                  id BIGSERIAL PRIMARY KEY,
+                                                  "card_style_num" int2,
+                                                  "card_style_desc" varchar(64) COLLATE "pg_catalog"."default",
+                                                  "style_url" varchar(255) COLLATE "pg_catalog"."default",
+                                                  "parent_id" int8,
+                                                  "create_user" varchar(64) COLLATE "pg_catalog"."default",
+                                                  "update_user" varchar(64) COLLATE "pg_catalog"."default",
+                                                  "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                  "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                  "del_flag" int2 NOT NULL DEFAULT 0
+)
+;
+
+ALTER TABLE "public"."t_card_style_template"
+    OWNER TO "postgres";
+
+COMMENT ON COLUMN "public"."t_card_style_template"."id" IS '主键ID';
+
+COMMENT ON COLUMN "public"."t_card_style_template"."card_style_num" IS '款式编号(枚举:1-普通款,2-设计款,3-高端款)';
+
+COMMENT ON COLUMN "public"."t_card_style_template"."card_style_desc" IS '款式描述(枚举:1-普通款,2-设计款,3-高端款)';
+
+COMMENT ON COLUMN "public"."t_card_style_template"."style_url" IS '款式图片地址';
+
+COMMENT ON COLUMN "public"."t_card_style_template"."parent_id" IS '父类id';
+
+COMMENT ON COLUMN "public"."t_card_style_template"."create_user" IS '创建人';
+
+COMMENT ON COLUMN "public"."t_card_style_template"."update_user" IS '修改人';
+
+COMMENT ON COLUMN "public"."t_card_style_template"."create_time" IS '创建时间';
+
+COMMENT ON COLUMN "public"."t_card_style_template"."update_time" IS '修改时间';
+
+COMMENT ON COLUMN "public"."t_card_style_template"."del_flag" IS '删除标识(0-未删,1-已删)';
+
+COMMENT ON TABLE "public"."t_card_style_template" IS '卡片设计款式模板表';
+
+
+-- 10.  Diamond Position 表(t_mold_number)
+CREATE TABLE "public"."t_mold_number" (
+                                          "id" BIGSERIAL PRIMARY KEY,
+                                          "mold_number" varchar(100) COLLATE "pg_catalog"."default" NOT NULL,
+                                          "order_no" varchar(100) COLLATE "pg_catalog"."default",
+                                          "user_id" int8,
+                                          "mold_name" varchar(200) COLLATE "pg_catalog"."default",
+                                          "mold_spec" varchar(200) COLLATE "pg_catalog"."default",
+                                          "mold_type" varchar(100) COLLATE "pg_catalog"."default",
+                                          "mold_status" int2 DEFAULT 0,
+                                          "mold_image_url" varchar(500) COLLATE "pg_catalog"."default",
+                                          "remark" varchar(500) COLLATE "pg_catalog"."default",
+                                          "usage_count" int4 DEFAULT 0,
+                                          "expected_usage_count" int4,
+                                          "mold_location" varchar(200) COLLATE "pg_catalog"."default",
+                                          "mold_supplier" varchar(200) COLLATE "pg_catalog"."default",
+                                          "purchase_date" date,
+                                          "warranty_period" int4,
+                                          "next_maintenance_date" date,
+                                          "create_user" varchar(50) COLLATE "pg_catalog"."default",
+                                          "update_user" varchar(50) COLLATE "pg_catalog"."default",
+                                          "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                          "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                          "del_flag" int2 DEFAULT 0,
+                                          CONSTRAINT "t_mold_number_pkey" PRIMARY KEY ("id")
+)
+;
+
+ALTER TABLE "public"."t_mold_number"
+    OWNER TO "postgres";
+
+CREATE INDEX "idx_mold_number" ON "public"."t_mold_number" USING btree (
+                                                                        "mold_number" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
+    );
+
+CREATE INDEX "idx_mold_order_no" ON "public"."t_mold_number" USING btree (
+                                                                          "order_no" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
+    );
+
+COMMENT ON COLUMN "public"."t_mold_number"."id" IS '主键ID';
+COMMENT ON COLUMN "public"."t_mold_number"."mold_number" IS '盒子编号';
+COMMENT ON COLUMN "public"."t_mold_number"."order_no" IS '订单编号';
+COMMENT ON COLUMN "public"."t_mold_number"."user_id" IS '用户ID';
+COMMENT ON COLUMN "public"."t_mold_number"."mold_status" IS '盒子状态(0-空闲,1-使用中,2-维修中,3-报废)';
+COMMENT ON COLUMN "public"."t_mold_number"."remark" IS '备注';
+COMMENT ON COLUMN "public"."t_mold_number"."create_user" IS '创建人';
+COMMENT ON COLUMN "public"."t_mold_number"."update_user" IS '修改人';
+COMMENT ON COLUMN "public"."t_mold_number"."create_time" IS '创建时间';
+COMMENT ON COLUMN "public"."t_mold_number"."update_time" IS '修改时间';
+COMMENT ON COLUMN "public"."t_mold_number"."del_flag" IS '逻辑删除标志(0-未删除,1-已删除)';
+COMMENT ON TABLE "public"."t_mold_number" IS '盒子编号表';
+
+
+ -- 11.  Diamond Position 表(t_order_merge_group)
+ -- 订单合并组表
+CREATE TABLE t_order_merge_group (
+                                   id BIGSERIAL PRIMARY KEY,
+                                   merge_group_no VARCHAR(64),
+                                   return_address_id int8,
+                                   receiver_name VARCHAR(64),
+                                   receiver_phone VARCHAR(32),
+                                   full_address VARCHAR(512),
+                                   logistics_company VARCHAR(64),
+                                   logistics_no VARCHAR(64),
+                                   latest_send_time timestamp(6),
+                                   merge_status INTEGER DEFAULT 0,
+                                   send_time TIMESTAMP,
+                                   create_time timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                   update_time timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                   create_user VARCHAR(64)
+);
+
+-- 表注释
+COMMENT ON TABLE t_order_merge_group IS '订单合并组表';
+
+-- 字段注释
+COMMENT ON COLUMN t_order_merge_group.id IS '主键ID';
+COMMENT ON COLUMN t_order_merge_group.merge_group_no IS '合并组号';
+COMMENT ON COLUMN t_order_merge_group.return_address_id IS '寄回地址ID';
+COMMENT ON COLUMN t_order_merge_group.receiver_name IS '收件人姓名';
+COMMENT ON COLUMN t_order_merge_group.receiver_phone IS '收件人电话';
+COMMENT ON COLUMN t_order_merge_group.full_address IS '完整地址';
+COMMENT ON COLUMN t_order_merge_group.logistics_company IS '物流公司';
+COMMENT ON COLUMN t_order_merge_group.logistics_no IS '物流单号';
+COMMENT ON COLUMN t_order_merge_group.latest_send_time IS '组内最晚发货时间';
+COMMENT ON COLUMN t_order_merge_group.merge_status IS '合并状态 0-待合并 1-已合并发货';
+COMMENT ON COLUMN t_order_merge_group.send_time IS '发货时间';
+COMMENT ON COLUMN t_order_merge_group.create_time IS '创建时间';
+COMMENT ON COLUMN t_order_merge_group.update_time IS '更新时间';
+COMMENT ON COLUMN t_order_merge_group.create_user IS '创建人';
+
+
+
+
+
+-- 12.  Diamond Position 表(t_sys_oper_log)
+CREATE TABLE "public"."t_sys_oper_log" (
+                                           "id" BIGSERIAL PRIMARY KEY,
+                                           "status" int4 NOT NULL DEFAULT 0,
+                                           "business_type" int4 NOT NULL DEFAULT 0,
+                                           "title" varchar(255) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
+                                           "oper_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                           "oper_ip" varchar(128) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
+                                           "method" varchar(255) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
+                                           "oper_url" varchar(255) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
+                                           "request_type" varchar(50) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
+                                           "request_method" varchar(10) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
+                                           "oper_param" varchar(2000) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
+                                           "json_result" varchar(2000) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
+                                           "error_msg" varchar(2000) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
+                                           "admin_id" int8 NOT NULL DEFAULT 0,
+                                           "admin_account" varchar(50) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
+                                           "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                           "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                           "del_flag" int4 NOT NULL DEFAULT 0
+)
+;
+
+ALTER TABLE "public"."t_sys_oper_log"
+    OWNER TO "postgres";
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."id" IS 'ID';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."status" IS '操作状态 0正常 1异常';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."business_type" IS '业务类型 0其它 1新增 2修改 3删除 4查询';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."title" IS '接口描述';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."oper_time" IS '操作时间';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."oper_ip" IS '主机地址';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."method" IS '方法名称';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."oper_url" IS '请求URL';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."request_type" IS '请求类型';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."request_method" IS '请求方式';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."oper_param" IS '请求参数';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."json_result" IS '返回参数';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."error_msg" IS '错误消息';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."admin_id" IS '管理员id';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."admin_account" IS '管理员账号';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."create_time" IS '创建时间';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."update_time" IS '修改时间';
+
+COMMENT ON COLUMN "public"."t_sys_oper_log"."del_flag" IS '删除标识 0正常 1删除';
+
+COMMENT ON TABLE "public"."t_sys_oper_log" IS '操作日志表';

+ 11 - 0
mango-application/src/main/resources/dozer/dozer-mapping.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping http://dozermapper.github.io/schema/bean-mapping.xsd">
+    <configuration>
+        <!--失败不抛出异常,继续转下一个字段-->
+        <stop-on-errors>false</stop-on-errors>
+        <!--排除映射空值-->
+        <map-null>false</map-null>
+    </configuration>
+</mappings>

+ 21 - 0
mango-application/src/main/resources/logback-fluentd.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<included>
+    <appender name="FLUENT_SYNC" class="ch.qos.logback.more.appenders.DataFluentAppender">
+        <tag>logback</tag>
+        <label>${spring.application.name}.${hostname}</label>
+        <remoteHost>${logging.fluentd.host}</remoteHost>
+        <port>${logging.fluentd.port}</port>
+
+        <encoder charset="UTF-8">
+            <pattern>%logger{15}:%L - %msg</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="FLUENT" class="ch.qos.logback.classic.AsyncAppender">
+        <queueSize>500</queueSize>
+        <neverBlock>true</neverBlock>
+        <maxFlushTime>15000</maxFlushTime>
+        <includeCallerData>false</includeCallerData>
+        <appender-ref ref="FLUENT_SYNC" />
+    </appender>
+</included>

+ 61 - 0
mango-application/src/main/resources/logback.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="../logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%X{TRACE_ID}] [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+    <springProperty scope="context" name="spring.application.name" source="spring.application.name"/>
+    <springProperty scope="context" name="logging.console.enabled" source="logging.console.enabled"/>
+    <springProperty scope="context" name="logging.file.enabled" source="logging.file.enabled"/>
+    <springProperty scope="context" name="logging.fluentd.enabled" source="logging.fluentd.enabled"/>
+    <springProperty scope="context" name="logging.fluentd.host" source="logging.fluentd.host"/>
+    <springProperty scope="context" name="logging.fluentd.port" source="logging.fluentd.port"/>
+
+    <define name="hostname" class="com.mangoo.rating.recommend.utils.HostPropertyDefiner"/>
+
+    <if condition='p("logging.fluentd.enabled").equals("true")'>
+        <then>
+            <include resource="logback-fluentd.xml" />
+        </then>
+    </if>
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+			<charset>UTF-8</charset>
+		</encoder>
+	</appender>
+
+
+	<!--系统操作日志-->
+    <root level="info">
+        <if condition='p("logging.console.enabled").equals("true")'>
+            <then>
+                <appender-ref ref="console" />
+            </then>
+        </if>
+        <if condition='p("logging.file.enabled").equals("true")'>
+            <then>
+                <appender-ref ref="file_info" />
+                <appender-ref ref="file_error" />
+            </then>
+        </if>
+        <if condition='p("logging.fluentd.enabled").equals("true")'>
+            <then>
+                <appender-ref ref="FLUENT"/>
+            </then>
+        </if>
+    </root>
+
+    <!-- Spring日志级别控制  -->
+    <logger name="org.springframework" level="warn" />
+    <logger name="org.apache.catalina" level="ERROR" />
+    <logger name="com.alibaba.druid" level="ERROR" />
+    <logger name="org.redisson" level="ERROR" />
+    <logger name="org.apache.coyote" level="ERROR" />
+    <logger name="io.lettuce.core" level="ERROR" />
+    <logger name="com.dtflys.forest" level="ERROR" />
+    <logger name="com.github" level="ERROR" />
+    <logger name="com.mangoo.rating.RatingRecommendlication" level="info" />
+    <logger name="com.poyee.domain.impl.DetailServiceImpl" level="WARN" />
+</configuration> 

+ 15 - 0
mango-application/src/main/resources/mybatis/mybatis-config.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration
+PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-config.dtd">
+<configuration>
+	
+	<settings>
+		<setting name="cacheEnabled"             value="true"  />  <!-- 全局映射器启用缓存 -->
+		<setting name="useGeneratedKeys"         value="true"  />  <!-- 允许 JDBC 支持自动生成主键 -->
+		<setting name="defaultExecutorType"      value="REUSE" />  <!-- 配置默认的执行器 -->
+		<setting name="logImpl"                  value="SLF4J" />  <!-- 指定 MyBatis 所用日志的具体实现 -->
+		<!-- <setting name="mapUnderscoreToCamelCase" value="true"/>  驼峰式命名 -->
+	</settings>
+	
+</configuration>

二進制
mango-application/src/main/resources/tessdata/chi_sim.traineddata


二進制
mango-application/src/main/resources/tessdata/eng.traineddata


+ 11 - 0
mango-application/src/test/java/com/mangoo/rating/recommend/test/RatingAppServiceTests.java

@@ -0,0 +1,11 @@
+package com.mangoo.rating.recommend.test;
+
+import com.mangoo.rating.recommend.RatingRecommendApplication;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@Slf4j
+@SpringBootTest(classes = RatingRecommendApplication.class)
+public class RatingAppServiceTests {
+
+}

+ 65 - 0
mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/AgentLoopExecutorTest.java

@@ -0,0 +1,65 @@
+package com.mangoo.rating.recommend.test.ai;
+
+import com.mangoo.rating.recommend.ai.agent.AgentLoopExecutor;
+import com.mangoo.rating.ai.model.*;
+import com.mangoo.rating.recommend.ai.model.*;
+import com.mangoo.rating.recommend.ai.provider.ChatModelProvider;
+import com.mangoo.rating.recommend.ai.tool.ToolRegistry;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+class AgentLoopExecutorTest {
+
+    @Test
+    void runs_tool_then_finishes_and_streams_text() {
+        ChatModelProvider provider = Mockito.mock(ChatModelProvider.class);
+        ToolRegistry registry = Mockito.mock(ToolRegistry.class);
+        when(registry.execute(eq("order_logistics_query"), anyString(), anyString()))
+            .thenReturn("订单RO1状态:送评中");
+
+        ChatResult r1 = ChatResult.builder().stopReason("tool_use")
+            .toolCalls(Collections.singletonList(
+                ToolCall.builder().id("tu1").name("order_logistics_query").argumentsJson("{}").build())).build();
+        ChatResult r2 = ChatResult.builder().stopReason("end_turn").text("您的订单正在送评中").build();
+        when(provider.chat(anyString(), anyList(), anyList(), any()))
+            .thenAnswer(inv -> { ((StreamCallback) inv.getArgument(3)).onTextDelta(""); return r1; })
+            .thenAnswer(inv -> { ((StreamCallback) inv.getArgument(3)).onTextDelta("您的订单正在送评中"); return r2; });
+
+        AgentLoopExecutor exec = new AgentLoopExecutor(provider, registry);
+        StringBuilder streamed = new StringBuilder();
+        List<ChatMessage> msgs = new ArrayList<>();
+        msgs.add(ChatMessage.builder().role(Role.USER).content("我的订单到哪了").build());
+
+        String finalText = exec.run("dummy-header", "sys", msgs, Collections.emptyList(), 5, streamed::append);
+
+        assertEquals("您的订单正在送评中", finalText);
+        assertEquals("您的订单正在送评中", streamed.toString());
+        verify(registry).execute(eq("order_logistics_query"), eq("{}"), anyString());
+        assertTrue(msgs.stream().anyMatch(m -> m.getRole() == Role.TOOL));
+    }
+
+    @Test
+    void stops_at_max_iterations() {
+        ChatModelProvider provider = Mockito.mock(ChatModelProvider.class);
+        ToolRegistry registry = Mockito.mock(ToolRegistry.class);
+        when(registry.execute(anyString(), anyString(), anyString())).thenReturn("x");
+        ChatResult loop = ChatResult.builder().stopReason("tool_use")
+            .toolCalls(Collections.singletonList(
+                ToolCall.builder().id("t").name("any").argumentsJson("{}").build())).build();
+        when(provider.chat(anyString(), anyList(), anyList(), any())).thenReturn(loop);
+
+        AgentLoopExecutor exec = new AgentLoopExecutor(provider, registry);
+        List<ChatMessage> msgs = new ArrayList<>();
+        msgs.add(ChatMessage.builder().role(Role.USER).content("hi").build());
+
+        String text = exec.run("dummy-header", "sys", msgs, Collections.emptyList(), 3, s -> {});
+        assertNotNull(text);
+        verify(provider, times(3)).chat(anyString(), anyList(), anyList(), any());
+    }
+}

+ 47 - 0
mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/AiChatServiceTest.java

@@ -0,0 +1,47 @@
+package com.mangoo.rating.recommend.test.ai;
+
+import com.mangoo.rating.recommend.ai.agent.AgentLoopExecutor;
+import com.mangoo.rating.ai.model.*;
+import com.mangoo.rating.recommend.ai.model.ChatMessage;
+import com.mangoo.rating.recommend.ai.model.Role;
+import com.mangoo.rating.recommend.ai.service.AiChatService;
+import com.mangoo.rating.recommend.ai.session.SessionContextStore;
+import com.mangoo.rating.recommend.ai.tool.ToolRegistry;
+import com.mangoo.rating.recommend.config.AiAssistantProperties;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+class AiChatServiceTest {
+
+    @Test
+    void loads_runs_and_saves_trimmed_context() {
+        SessionContextStore store = Mockito.mock(SessionContextStore.class);
+        AgentLoopExecutor exec = Mockito.mock(AgentLoopExecutor.class);
+        ToolRegistry registry = Mockito.mock(ToolRegistry.class);
+        AiAssistantProperties props = new AiAssistantProperties();
+        when(store.load("s1")).thenReturn(new ArrayList<>());
+        when(registry.specs()).thenReturn(Collections.emptyList());
+        when(exec.run(anyString(), anyString(), anyList(), anyList(), anyInt(), any()))
+            .thenReturn("您的订单正在送评中");
+
+        AiChatService svc = new AiChatService(store, exec, registry, props);
+        StringBuilder streamed = new StringBuilder();
+        svc.chat("dummy-header", "s1", "我的订单到哪了", streamed::append);
+
+        ArgumentCaptor<List<ChatMessage>> cap = ArgumentCaptor.forClass(List.class);
+        verify(store).save(eq("s1"), cap.capture(), anyInt());
+        List<ChatMessage> saved = cap.getValue();
+        assertEquals(2, saved.size());
+        assertEquals(Role.USER, saved.get(0).getRole());
+        assertEquals(Role.ASSISTANT, saved.get(1).getRole());
+        assertEquals("您的订单正在送评中", saved.get(1).getContent());
+        assertTrue(saved.stream().noneMatch(m -> m.getRole() == Role.TOOL));
+    }
+}

+ 56 - 0
mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/ClaudeSseParserTest.java

@@ -0,0 +1,56 @@
+package com.mangoo.rating.recommend.test.ai;
+
+import com.mangoo.rating.recommend.ai.provider.ClaudeChatProvider;
+import com.mangoo.rating.recommend.ai.model.ChatResult;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Claude SSE 解析纯逻辑单测(不触网,仅验证 feedLine + SseAccumulator)。
+ * 覆盖两类场景:纯文本 end_turn、tool_use 工具调用。
+ */
+class ClaudeSseParserTest {
+
+    /** 文本增量逐段累积,message_delta 携带 end_turn */
+    @Test
+    void parses_text_delta_and_end_turn() {
+        List<String> lines = new ArrayList<>();
+        lines.add("data: {\"type\":\"content_block_delta\",\"delta\":{\"type\":\"text_delta\",\"text\":\"您好\"}}");
+        lines.add("data: {\"type\":\"content_block_delta\",\"delta\":{\"type\":\"text_delta\",\"text\":\",订单送评中\"}}");
+        lines.add("data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\"}}");
+
+        // 同时校验流式回调推送内容与最终结果一致
+        StringBuilder streamed = new StringBuilder();
+        ClaudeChatProvider.SseAccumulator acc = new ClaudeChatProvider.SseAccumulator(streamed::append);
+        for (String l : lines) { ClaudeChatProvider.feedLine(l, acc); }
+        ChatResult r = acc.toResult();
+
+        assertEquals("end_turn", r.getStopReason());
+        assertEquals("您好,订单送评中", r.getText());
+        assertEquals("您好,订单送评中", streamed.toString());
+        assertTrue(r.getToolCalls() == null || r.getToolCalls().isEmpty());
+    }
+
+    /** 工具调用:content_block_start 起一个 tool_use,input_json_delta 拼参数,end 为 tool_use */
+    @Test
+    void parses_tool_use_block() {
+        List<String> lines = new ArrayList<>();
+        lines.add("data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"tu_1\",\"name\":\"order_logistics_query\"}}");
+        lines.add("data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"orderNo\\\":\\\"RO1\\\"}\"}}");
+        lines.add("data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\"}}");
+
+        ClaudeChatProvider.SseAccumulator acc = new ClaudeChatProvider.SseAccumulator(s -> {});
+        for (String l : lines) { ClaudeChatProvider.feedLine(l, acc); }
+        ChatResult r = acc.toResult();
+
+        assertEquals("tool_use", r.getStopReason());
+        assertEquals(1, r.getToolCalls().size());
+        assertEquals("order_logistics_query", r.getToolCalls().get(0).getName());
+        assertEquals("tu_1", r.getToolCalls().get(0).getId());
+        assertTrue(r.getToolCalls().get(0).getArgumentsJson().contains("RO1"));
+    }
+}

+ 56 - 0
mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/OrderLogisticsQueryToolTest.java

@@ -0,0 +1,56 @@
+package com.mangoo.rating.recommend.test.ai;
+
+import com.mangoo.rating.recommend.AjaxResult;
+import com.mangoo.rating.recommend.ai.tool.OrderLogisticsQueryTool;
+import com.mangoo.rating.recommend.client.feign.RatingOrderFeignClient;
+import com.mangoo.rating.recommend.page.TableDataInfo;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+/**
+ * 订单物流查询工具单元测试(Feign 远程调用,Mockito 打桩)。
+ * 验证:① 有订单时组装出含订单号/状态的文本;② 无订单时返回友好提示。
+ */
+class OrderLogisticsQueryToolTest {
+
+    /** 有最近订单:应组装出包含订单号与状态描述的结果文本 */
+    @Test
+    void query_recent_order_assembles_text() {
+        RatingOrderFeignClient feign = Mockito.mock(RatingOrderFeignClient.class);
+        // 打桩:我的订单列表返回一条记录
+        Map<String, Object> row = new HashMap<>();
+        row.put("orderId", 1001L); row.put("orderNo", "RO20260617001"); row.put("statusText", "送评中");
+        TableDataInfo page = new TableDataInfo();
+        page.setRows(Collections.singletonList(row));
+        when(feign.myOrderList(anyString(), any())).thenReturn(page);
+
+        // 打桩:订单详情成功(code=0),data 含物流状态描述
+        Map<String, Object> data = new HashMap<>();
+        data.put("orderNo", "RO20260617001"); data.put("orderStatusDesc", "送评中");
+        AjaxResult detail = new AjaxResult();
+        detail.put("code", 0); detail.put("data", data);
+        when(feign.orderDetail(anyString(), any())).thenReturn(detail);
+
+        OrderLogisticsQueryTool tool = new OrderLogisticsQueryTool(feign);
+        String text = tool.execute("{}", "dummy-header");
+        assertTrue(text.contains("RO20260617001"));
+        assertTrue(text.contains("送评中"));
+    }
+
+    /** 无订单:应返回包含「未查询到」的友好提示 */
+    @Test
+    void no_order_returns_friendly_text() {
+        RatingOrderFeignClient feign = Mockito.mock(RatingOrderFeignClient.class);
+        TableDataInfo empty = new TableDataInfo();
+        empty.setRows(Collections.emptyList());
+        when(feign.myOrderList(anyString(), any())).thenReturn(empty);
+        OrderLogisticsQueryTool tool = new OrderLogisticsQueryTool(feign);
+        assertTrue(tool.execute("{}", "dummy-header").contains("未查询到"));
+    }
+}

+ 55 - 0
mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/SessionContextStoreTest.java

@@ -0,0 +1,55 @@
+package com.mangoo.rating.recommend.test.ai;
+
+import com.alibaba.fastjson.JSON;
+import com.mangoo.rating.recommend.ai.model.ChatMessage;
+import com.mangoo.rating.recommend.ai.model.Role;
+import com.mangoo.rating.recommend.ai.session.SessionContextStore;
+import com.mangoo.rating.recommend.utils.RedisUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+class SessionContextStoreTest {
+
+    @Test
+    void save_writes_json_with_ttl_seconds() {
+        RedisUtils redis = Mockito.mock(RedisUtils.class);
+        SessionContextStore store = new SessionContextStore(redis);
+        List<ChatMessage> msgs = Collections.singletonList(
+            ChatMessage.builder().role(Role.USER).content("hi").build());
+        store.save("s1", msgs, 30);
+        ArgumentCaptor<String> key = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<Object> val = ArgumentCaptor.forClass(Object.class);
+        ArgumentCaptor<Long> ttl = ArgumentCaptor.forClass(Long.class);
+        verify(redis).set(key.capture(), val.capture(), ttl.capture());
+        assertEquals("ai:chat:session:s1", key.getValue());
+        assertEquals(30L * 60, ttl.getValue());
+        assertTrue(((String) val.getValue()).contains("hi"));
+    }
+
+    @Test
+    void load_parses_json() {
+        RedisUtils redis = Mockito.mock(RedisUtils.class);
+        List<ChatMessage> seed = Collections.singletonList(
+            ChatMessage.builder().role(Role.ASSISTANT).content("hello").build());
+        when(redis.get("ai:chat:session:s1")).thenReturn(JSON.toJSONString(seed));
+        SessionContextStore store = new SessionContextStore(redis);
+        List<ChatMessage> got = store.load("s1");
+        assertEquals(1, got.size());
+        assertEquals("hello", got.get(0).getContent());
+    }
+
+    @Test
+    void load_missing_returns_empty() {
+        RedisUtils redis = Mockito.mock(RedisUtils.class);
+        when(redis.get(anyString())).thenReturn(null);
+        SessionContextStore store = new SessionContextStore(redis);
+        assertTrue(store.load("none").isEmpty());
+    }
+}

+ 34 - 0
mango-application/src/test/java/com/mangoo/rating/recommend/test/ai/ToolRegistryTest.java

@@ -0,0 +1,34 @@
+package com.mangoo.rating.recommend.test.ai;
+
+import com.mangoo.rating.recommend.ai.model.ToolSpec;
+import com.mangoo.rating.recommend.ai.tool.AiTool;
+import com.mangoo.rating.recommend.ai.tool.ToolRegistry;
+import org.junit.jupiter.api.Test;
+import java.util.Collections;
+import java.util.List;
+import static org.junit.jupiter.api.Assertions.*;
+
+class ToolRegistryTest {
+    static class EchoTool implements AiTool {
+        public String name() { return "echo"; }
+        public String description() { return "回显入参"; }
+        public String inputSchemaJson() { return "{\"type\":\"object\",\"properties\":{}}"; }
+        public String execute(String argumentsJson, String userHeader) { return "echo:" + argumentsJson; }
+    }
+
+    @Test
+    void specs_and_route_execute() {
+        ToolRegistry reg = new ToolRegistry(Collections.singletonList(new EchoTool()));
+        List<ToolSpec> specs = reg.specs();
+        assertEquals(1, specs.size());
+        assertEquals("echo", specs.get(0).getName());
+        assertEquals("echo:{}", reg.execute("echo", "{}", "dummy-header"));
+    }
+
+    @Test
+    void unknown_tool_returns_error_text_not_throw() {
+        ToolRegistry reg = new ToolRegistry(Collections.singletonList(new EchoTool()));
+        String r = reg.execute("not_exist", "{}", "dummy-header");
+        assertTrue(r.contains("未知工具"));
+    }
+}

+ 355 - 0
mango-common/pom.xml

@@ -0,0 +1,355 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.poyee</groupId>
+        <artifactId>ratingRecommend</artifactId>
+        <version>1.0.0</version>
+    </parent>
+
+
+    <artifactId>mango-common</artifactId>
+    <version>1.0.0</version>
+
+    <properties>
+        <pagehelper.boot.version>1.3.0</pagehelper.boot.version>
+        <shiro.version>1.6.0</shiro.version>
+        <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.github.dozermapper</groupId>
+            <artifactId>dozer-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.qiniu</groupId>
+            <artifactId>qiniu-java-sdk</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+        </dependency>
+
+<!--        <dependency>-->
+<!--            <groupId>io.springfox</groupId>-->
+<!--            <artifactId>springfox-swagger2</artifactId>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>io.swagger</groupId>-->
+<!--            <artifactId>swagger-annotations</artifactId>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>io.swagger</groupId>-->
+<!--            <artifactId>swagger-models</artifactId>-->
+<!--        </dependency>-->
+
+        <!-- knife4j 接口管理 -->
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-spring-boot-starter</artifactId>
+            <version>2.0.9</version>
+        </dependency>
+        <!-- swagger2-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>2.10.5</version>
+            <!--<exclusions>
+                <exclusion>
+                    <groupId>io.swagger</groupId>
+                    <artifactId>swagger-annotations</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>io.swagger</groupId>
+                    <artifactId>swagger-models</artifactId>
+                </exclusion>
+            </exclusions>-->
+        </dependency>
+        <!--防止进入swagger页面报类型转换错误,排除2.9.2中的引用,手动增加1.5.21版本-->
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>1.5.21</version>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+            <version>1.5.21</version>
+        </dependency>
+        <!-- swagger2-UI-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>2.9.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>swagger-bootstrap-ui</artifactId>
+            <version>1.9.5</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.dtflys.forest</groupId>
+            <artifactId>forest-spring-boot-starter</artifactId>
+        </dependency>
+
+<!--        <dependency>-->
+<!--            <groupId>org.elasticsearch.client</groupId>-->
+<!--            <artifactId>elasticsearch-rest-high-level-client</artifactId>-->
+<!--            <exclusions>-->
+<!--                <exclusion>-->
+<!--                    <groupId>net.java.dev.jna</groupId>-->
+<!--                    <artifactId>jna</artifactId>-->
+<!--                </exclusion>-->
+<!--                <exclusion>-->
+<!--                    <groupId>net.java.dev.jna</groupId>-->
+<!--                    <artifactId>jna-platform</artifactId>-->
+<!--                </exclusion>-->
+<!--            </exclusions>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>org.springframework.data</groupId>
+            <artifactId>spring-data-elasticsearch</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.elasticsearch</groupId>
+                    <artifactId>jna</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
+            <version>8.0.3</version>
+        </dependency>
+        <!--常用工具类 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <!-- pagehelper 分页插件 -->
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <version>${pagehelper.boot.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>3.3.2</version>
+        </dependency>
+
+        <!-- Hutool 核心包(最常用,包含90%的工具类) -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-core</artifactId>
+            <version>5.8.26</version>
+        </dependency>
+
+        <!-- 可选:根据业务按需引入子模块,需要哪个加哪个 -->
+        <!-- 1. 工具类大全(包含所有模块,懒人推荐直接引入这个) -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.26</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-amqp</artifactId>
+        </dependency>
+
+        <!-- MapStruct核心依赖 -->
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+            <version>1.5.5.Final</version>
+        </dependency>
+        <!-- MapStruct注解处理器 必须加,核心!编译期生成代码 -->
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-processor</artifactId>
+            <version>1.5.5.Final</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- 阿里Cola StateMachine核心依赖 -->
+        <dependency>
+            <groupId>com.alibaba.cola</groupId>
+            <artifactId>cola-component-statemachine</artifactId>
+            <version>4.3.1</version>
+        </dependency>
+
+        <!-- 腾讯云COS依赖 -->
+        <dependency>
+            <groupId>com.qcloud</groupId>
+            <artifactId>cos_api</artifactId>
+            <version>5.6.257</version>
+        </dependency>
+
+        <!-- 解决 tess4j JNA 版本冲突 -->
+        <dependency>
+            <groupId>net.java.dev.jna</groupId>
+            <artifactId>jna</artifactId>
+            <version>5.5.0</version>
+        </dependency>
+        <dependency>
+            <groupId>net.java.dev.jna</groupId>
+            <artifactId>jna-platform</artifactId>
+            <version>5.5.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>net.sourceforge.tess4j</groupId>
+            <artifactId>tess4j</artifactId>
+            <version>5.12.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>net.java.dev.jna</groupId>
+                    <artifactId>jna</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>net.java.dev.jna</groupId>
+                    <artifactId>jna-platform</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!--接入 顺丰 丰桥sdk-->
+        <dependency>
+            <groupId>com.sf</groupId>
+            <artifactId>sf-csim-express-sdk</artifactId>
+            <version>2.1.5</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/lib/sf-csim-express-sdk-V2.1.5.jar</systemPath>
+        </dependency>
+        <!--中通 sdk-->
+        <dependency>
+            <groupId>com.zto.zop</groupId>
+            <artifactId>zopsdk</artifactId>
+            <version>0.6</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/lib/zopsdk-0.6.jar</systemPath>
+        </dependency>
+        <!--接入 韵达sdk-->
+        <dependency>
+            <groupId>com.yundasys</groupId>
+            <artifactId>kfpt-sdk</artifactId>
+            <version>1.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/lib/kfpt-sdk-1.0.0.jar</systemPath>
+        </dependency>
+        <!--接入 京东物流sdk -基础sdk -->
+        <dependency>
+            <groupId>com.lop</groupId>
+            <artifactId>lop-opensdk-support</artifactId>
+            <version>1.0.23</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/lib/lop-opensdk-support-1.0.23.jar</systemPath>
+        </dependency>
+        <!--接入 京东物流sdk -业务sdk -->
+        <dependency>
+            <groupId>com.lop</groupId>
+            <artifactId>Express</artifactId>
+            <version>1.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/lib/Express-1.0.0.jar</systemPath>
+        </dependency>
+        <!--接入 京东快递【商家快递】 -业务sdk -->
+        <dependency>
+            <groupId>com.lop</groupId>
+            <artifactId>ExpressB2C</artifactId>
+            <version>2.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/lib/ExpressB2C-2.0.0.jar</systemPath>
+        </dependency>
+        <!--  银联商务-沈阳sdk     -->
+        <dependency>
+            <groupId>com.jeequan.jeepay</groupId>
+            <artifactId>pay-sdk-java</artifactId>
+            <version>1.5.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/lib/pay-sdk-java-1.5.0.jar</systemPath>
+        </dependency>
+
+        <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>nimbus-jose-jwt</artifactId>
+            <version>9.31</version>
+        </dependency>
+
+
+        <!--Shiro核心框架 -->
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-core</artifactId>
+            <version>${shiro.version}</version>
+        </dependency>
+
+        <!-- Shiro使用Spring框架 -->
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-spring</artifactId>
+            <version>${shiro.version}</version>
+        </dependency>
+
+        <!-- Shiro使用EhCache缓存框架 -->
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-ehcache</artifactId>
+            <version>${shiro.version}</version>
+        </dependency>
+
+        <!-- 解析客户端操作系统、浏览器等 -->
+        <dependency>
+            <groupId>eu.bitwalker</groupId>
+            <artifactId>UserAgentUtils</artifactId>
+            <version>${bitwalker.version}</version>
+        </dependency>
+
+        <!-- MyBatis-Plus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>${mybatis-plus.version}</version>
+            <!-- 排除自带的旧版 jsqlparser -->
+            <exclusions>
+                <exclusion>
+                    <groupId>com.github.jsqlparser</groupId>
+                    <artifactId>jsqlparser</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 254 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/AjaxResult.java

@@ -0,0 +1,254 @@
+package com.mangoo.rating.recommend;
+
+import com.github.pagehelper.PageInfo;
+import com.mangoo.rating.recommend.utils.StringUtils;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * 操作消息提醒
+ *
+ * @author zheng
+ */
+public class AjaxResult extends HashMap<String, Object>
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 状态码 */
+    public static final String CODE_TAG = "code";
+
+    /** 返回内容 */
+    public static final String MSG_TAG = "msg";
+
+    /** 数据对象 */
+    public static final String DATA_TAG = "data";
+
+    /**
+     * 状态类型
+     */
+    public enum Type
+    {
+        /** 成功 */
+        SUCCESS(0),
+        /** 警告 */
+        WARN(301),
+        /** 错误 */
+        ERROR(-1);
+        private final int value;
+
+        Type(int value)
+        {
+            this.value = value;
+        }
+
+        public int value()
+        {
+            return this.value;
+        }
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
+     */
+    public AjaxResult()
+    {
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     *
+     * @param type 状态类型
+     * @param msg 返回内容
+     */
+    public AjaxResult(Type type, String msg)
+    {
+        super.put(CODE_TAG, type.value);
+        super.put(MSG_TAG, msg);
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     *
+     * @param code 状态类型
+     * @param msg 返回内容
+     */
+    public AjaxResult(Integer code, String msg)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     *
+     * @param type 状态类型
+     * @param msg 返回内容
+     * @param data 数据对象
+     */
+    public AjaxResult(Type type, String msg, Object data)
+    {
+        super.put(CODE_TAG, type.value);
+        super.put(MSG_TAG, msg);
+        if (StringUtils.isNotNull(data))
+        {
+            super.put(DATA_TAG, data);
+        }
+    }
+
+    /**
+     * 方便链式调用
+     *
+     * @param key 键
+     * @param value 值
+     * @return 数据对象
+     */
+    @Override
+    public AjaxResult put(String key, Object value)
+    {
+        super.put(key, value);
+        return this;
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @return 成功消息
+     */
+    public static AjaxResult success()
+    {
+        return AjaxResult.success("操作成功");
+    }
+
+    /**
+     * 返回成功数据
+     *
+     * @return 成功消息
+     */
+    public static AjaxResult success(Object data)
+    {
+        return AjaxResult.success("操作成功", data);
+    }
+
+    public static AjaxResult successPage(List<?> data)
+    {
+        return AjaxResult.success("操作成功", new PageInfo<>(data));
+    }
+
+
+
+
+
+    /**
+     * 返回成功消息
+     *
+     * @param msg 返回内容
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg)
+    {
+        return AjaxResult.success(msg, null);
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg, Object data)
+    {
+        return new AjaxResult(Type.SUCCESS, msg, data);
+    }
+
+    /**
+     * 返回警告消息
+     *
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult warn(String msg)
+    {
+        return AjaxResult.warn(msg, null);
+    }
+
+    /**
+     * 返回警告消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static AjaxResult warn(String msg, Object data)
+    {
+        return new AjaxResult(Type.WARN, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @return
+     */
+    public static AjaxResult error()
+    {
+        return AjaxResult.error("操作失败");
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult error(String msg)
+    {
+        return AjaxResult.error(msg, null);
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @param code 返回内容
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult error(Integer code,String msg)
+    {
+        return new AjaxResult(code, msg);
+    }
+
+    /**
+     * 自定义返回错误消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static AjaxResult error(String msg, Object data)
+    {
+        return new AjaxResult(Type.ERROR, msg, data);
+    }
+
+    /**
+     * 获取状态码(类型安全,避免手动强转)
+     *
+     * @return 状态码
+     */
+    public int getCode() {
+        Object code = super.get(CODE_TAG);
+        return code == null ? Type.ERROR.value() : (Integer) code;
+    }
+
+
+    /**
+     * 获取返回消息(类型安全,避免空指针)
+     *
+     * @return 返回消息
+     */
+    public String getMsg() {
+        Object msg = super.get(MSG_TAG);
+        return msg == null ? "" : msg.toString();
+    }
+
+}

+ 239 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/BaseEntity.java

@@ -0,0 +1,239 @@
+package com.mangoo.rating.recommend;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.mangoo.rating.recommend.utils.StringUtils;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Entity基类
+ * 
+ * @author zheng
+ */
+public class BaseEntity implements Serializable
+{
+
+    /** 搜索值 */
+    @ApiModelProperty(hidden = true)
+    @ExcelIgnore
+    private String searchValue;
+    
+    /**
+     * 字段查询
+     */
+    @ApiModelProperty(hidden = true)
+    @ExcelIgnore
+    private String columnsearch;
+
+
+	/** 创建者 */
+    @ApiModelProperty(hidden = true)
+    @ExcelIgnore
+    private String createBy;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(hidden = true)
+    @ExcelIgnore
+    private Date createTime;
+
+    /** 更新者 */
+    @ApiModelProperty(hidden = true)
+    @ExcelIgnore
+    private String updateBy;
+
+    /** 更新时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(hidden = true)
+    @ExcelIgnore
+    private Date updateTime;
+
+    /** 备注 */
+    @ApiModelProperty(hidden = true)
+    @ExcelIgnore
+    private String remark;
+
+    /** 请求参数 */
+    @ApiModelProperty(hidden = true)
+    @ExcelIgnore
+    private Map<String, Object> params;
+
+    @ApiModelProperty(hidden = true)
+    @ExcelIgnore
+    private String FileType;
+
+    //分页
+    @ExcelIgnore
+    @ApiModelProperty(value =  "当前页")
+    private Integer page;
+
+    @ExcelIgnore
+    @ApiModelProperty(value =  "分页大小")
+    private Integer rows;
+    //排序
+    @ExcelIgnore
+    private String sidx;
+
+    @ExcelIgnore
+    private String sord;
+
+    @ExcelIgnore
+    private Integer start;
+
+    @ExcelIgnore
+    private Integer end;
+    @ExcelIgnore
+    private Integer offset;
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public Integer getRows() {
+        return rows;
+    }
+
+    public void setRows(Integer rows) {
+        this.rows = rows;
+    }
+
+    public String getSidx() {
+        return sidx;
+    }
+
+    public void setSidx(String sidx) {
+        this.sidx = sidx;
+    }
+
+    public String getSord() {
+        return sord;
+    }
+
+    public void setSord(String sord) {
+        this.sord = sord;
+    }
+
+    public String getSearchValue()
+    {
+        return searchValue;
+    }
+
+    public void setSearchValue(String searchValue)
+    {
+        this.searchValue = searchValue;
+    }
+
+    public String getColumnsearch() {
+        if(StringUtils.isNotEmpty(columnsearch))return this.columnsearch.replaceAll("☻", "%");
+		return columnsearch;
+	}
+
+	public void setColumnsearch(String columnsearch) {
+		this.columnsearch = columnsearch;
+	}
+	
+    public String getCreateBy()
+    {
+        return createBy;
+    }
+
+    public void setCreateBy(String createBy)
+    {
+        this.createBy = createBy;
+    }
+
+    public Date getCreateTime()
+    {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime)
+    {
+        this.createTime = createTime;
+    }
+
+    public String getUpdateBy()
+    {
+        return updateBy;
+    }
+
+    public void setUpdateBy(String updateBy)
+    {
+        this.updateBy = updateBy;
+    }
+
+    public Date getUpdateTime()
+    {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime)
+    {
+        this.updateTime = updateTime;
+    }
+
+    public String getRemark()
+    {
+        return remark;
+    }
+
+    public void setRemark(String remark)
+    {
+        this.remark = remark;
+    }
+
+    public Map<String, Object> getParams()
+    {
+        if (params == null)
+        {
+            params = new HashMap<>();
+        }
+        return params;
+    }
+
+    public void setParams(Map<String, Object> params)
+    {
+        this.params = params;
+    }
+
+    public Integer getStart() {
+        return start;
+    }
+
+    public void setStart(Integer start) {
+        this.start = start;
+    }
+
+    public Integer getEnd() {
+        return end;
+    }
+
+    public void setEnd(Integer end) {
+        this.end = end;
+    }
+
+    public String getFileType() {
+        return FileType;
+    }
+
+    public void setFileType(String fileType) {
+        FileType = fileType;
+    }
+
+    public Integer getOffset() {
+        return offset;
+    }
+
+    public void setOffset(Integer offset) {
+        this.offset = offset;
+    }
+}

+ 24 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/BaseParam.java

@@ -0,0 +1,24 @@
+package com.mangoo.rating.recommend;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Entity基类
+ * 
+ * @author zheng
+ */
+@Data
+public class BaseParam implements Serializable {
+
+    /** 搜索 */
+    protected String keyword;
+    //分页
+    protected Integer page=1;
+    protected Integer rows=10;
+    //排序
+    protected String sidx;
+    /**升序降序desc*/
+    protected String sord="DESC";
+}

+ 88 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/CharsetKit.java

@@ -0,0 +1,88 @@
+package com.mangoo.rating.recommend;
+
+
+import com.mangoo.rating.recommend.utils.StringUtils;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 字符集工具类
+ * 
+ * @author zheng
+ */
+public class CharsetKit
+{
+    /** ISO-8859-1 */
+    public static final String ISO_8859_1 = "ISO-8859-1";
+    /** UTF-8 */
+    public static final String UTF_8 = "UTF-8";
+    /** GBK */
+    public static final String GBK = "GBK";
+
+    /** ISO-8859-1 */
+    public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
+    /** UTF-8 */
+    public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
+    /** GBK */
+    public static final Charset CHARSET_GBK = Charset.forName(GBK);
+
+    /**
+     * 转换为Charset对象
+     * 
+     * @param charset 字符集,为空则返回默认字符集
+     * @return Charset
+     */
+    public static Charset charset(String charset)
+    {
+        return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
+    }
+
+    /**
+     * 转换字符串的字符集编码
+     * 
+     * @param source 字符串
+     * @param srcCharset 源字符集,默认ISO-8859-1
+     * @param destCharset 目标字符集,默认UTF-8
+     * @return 转换后的字符集
+     */
+    public static String convert(String source, String srcCharset, String destCharset)
+    {
+        return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
+    }
+
+    /**
+     * 转换字符串的字符集编码
+     * 
+     * @param source 字符串
+     * @param srcCharset 源字符集,默认ISO-8859-1
+     * @param destCharset 目标字符集,默认UTF-8
+     * @return 转换后的字符集
+     */
+    public static String convert(String source, Charset srcCharset, Charset destCharset)
+    {
+        if (null == srcCharset)
+        {
+            srcCharset = StandardCharsets.ISO_8859_1;
+        }
+
+        if (null == destCharset)
+        {
+            srcCharset = StandardCharsets.UTF_8;
+        }
+
+        if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))
+        {
+            return source;
+        }
+        return new String(source.getBytes(srcCharset), destCharset);
+    }
+
+    /**
+     * @return 系统字符集编码
+     */
+    public static String systemCharset()
+    {
+        return Charset.defaultCharset().name();
+    }
+}

+ 1001 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/Convert.java

@@ -0,0 +1,1001 @@
+package com.mangoo.rating.recommend;
+
+
+import com.mangoo.rating.recommend.utils.StringUtils;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.text.NumberFormat;
+import java.util.Set;
+
+/**
+ * 类型转换器
+ * 
+ * @author zheng
+ */
+public class Convert
+{
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static String toStr(Object value, String defaultValue)
+    {
+        if (null == value)
+        {
+            return defaultValue;
+        }
+        if (value instanceof String)
+        {
+            return (String) value;
+        }
+        return value.toString();
+    }
+
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static String toStr(Object value)
+    {
+        return toStr(value, null);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Character toChar(Object value, Character defaultValue)
+    {
+        if (null == value)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Character)
+        {
+            return (Character) value;
+        }
+
+        final String valueStr = toStr(value, null);
+        return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Character toChar(Object value)
+    {
+        return toChar(value, null);
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Byte toByte(Object value, Byte defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Byte)
+        {
+            return (Byte) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).byteValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Byte.parseByte(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Byte toByte(Object value)
+    {
+        return toByte(value, null);
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Short toShort(Object value, Short defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Short)
+        {
+            return (Short) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).shortValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Short.parseShort(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Short toShort(Object value)
+    {
+        return toShort(value, null);
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Number toNumber(Object value, Number defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Number)
+        {
+            return (Number) value;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return NumberFormat.getInstance().parse(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Number toNumber(Object value)
+    {
+        return toNumber(value, null);
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Integer toInt(Object value, Integer defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Integer)
+        {
+            return (Integer) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).intValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Integer.parseInt(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Integer toInt(Object value)
+    {
+        return toInt(value, null);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     * 
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String str)
+    {
+        return toIntArray(",", str);
+    }
+
+    /**
+     * 转换为Long数组<br>
+     * 
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String str)
+    {
+        return toLongArray(",", str);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     * 
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String split, String str)
+    {
+        if (StringUtils.isEmpty(str))
+        {
+            return new Integer[] {};
+        }
+        String[] arr = str.split(split);
+        final Integer[] ints = new Integer[arr.length];
+        for (int i = 0; i < arr.length; i++)
+        {
+            final Integer v = toInt(arr[i], 0);
+            ints[i] = v;
+        }
+        return ints;
+    }
+
+    /**
+     * 转换为Long数组<br>
+     * 
+     * @param split 分隔符
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String split, String str)
+    {
+        if (StringUtils.isEmpty(str))
+        {
+            return new Long[] {};
+        }
+        String[] arr = str.split(split);
+        final Long[] longs = new Long[arr.length];
+        for (int i = 0; i < arr.length; i++)
+        {
+            final Long v = toLong(arr[i], null);
+            longs[i] = v;
+        }
+        return longs;
+    }
+
+    /**
+     * 转换为String数组<br>
+     * 
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String str)
+    {
+        return toStrArray(",", str);
+    }
+
+    /**
+     * 转换为String数组<br>
+     * 
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String split, String str)
+    {
+        return str.split(split);
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Long toLong(Object value, Long defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Long)
+        {
+            return (Long) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).longValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).longValue();
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Long toLong(Object value)
+    {
+        return toLong(value, null);
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Double toDouble(Object value, Double defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Double)
+        {
+            return (Double) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).doubleValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).doubleValue();
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Double toDouble(Object value)
+    {
+        return toDouble(value, null);
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Float toFloat(Object value, Float defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Float)
+        {
+            return (Float) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).floatValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Float.parseFloat(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Float toFloat(Object value)
+    {
+        return toFloat(value, null);
+    }
+
+    /**
+     * 转换为boolean<br>
+     * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value, Boolean defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Boolean)
+        {
+            return (Boolean) value;
+        }
+        String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        valueStr = valueStr.trim().toLowerCase();
+        switch (valueStr)
+        {
+            case "true":
+                return true;
+            case "false":
+                return false;
+            case "yes":
+                return true;
+            case "ok":
+                return true;
+            case "no":
+                return false;
+            case "1":
+                return true;
+            case "0":
+                return false;
+            default:
+                return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为boolean<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value)
+    {
+        return toBool(value, null);
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 
+     * @param clazz Enum的Class
+     * @param value 值
+     * @param defaultValue 默认值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (clazz.isAssignableFrom(value.getClass()))
+        {
+            @SuppressWarnings("unchecked")
+            E myE = (E) value;
+            return myE;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Enum.valueOf(clazz, valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 
+     * @param clazz Enum的Class
+     * @param value 值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value)
+    {
+        return toEnum(clazz, value, null);
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value, BigInteger defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof BigInteger)
+        {
+            return (BigInteger) value;
+        }
+        if (value instanceof Long)
+        {
+            return BigInteger.valueOf((Long) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return new BigInteger(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value)
+    {
+        return toBigInteger(value, null);
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof BigDecimal)
+        {
+            return (BigDecimal) value;
+        }
+        if (value instanceof Long)
+        {
+            return new BigDecimal((Long) value);
+        }
+        if (value instanceof Double)
+        {
+            return new BigDecimal((Double) value);
+        }
+        if (value instanceof Integer)
+        {
+            return new BigDecimal((Integer) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return new BigDecimal(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value)
+    {
+        return toBigDecimal(value, null);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     * 
+     * @param obj 对象
+     * @return 字符串
+     */
+    public static String utf8Str(Object obj)
+    {
+        return str(obj, CharsetKit.CHARSET_UTF_8);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     * 
+     * @param obj 对象
+     * @param charsetName 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, String charsetName)
+    {
+        return str(obj, Charset.forName(charsetName));
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     * 
+     * @param obj 对象
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, Charset charset)
+    {
+        if (null == obj)
+        {
+            return null;
+        }
+
+        if (obj instanceof String)
+        {
+            return (String) obj;
+        }
+        else if (obj instanceof byte[] || obj instanceof Byte[])
+        {
+            return str((Byte[]) obj, charset);
+        }
+        else if (obj instanceof ByteBuffer)
+        {
+            return str((ByteBuffer) obj, charset);
+        }
+        return obj.toString();
+    }
+
+    /**
+     * 将byte数组转为字符串
+     * 
+     * @param bytes byte数组
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(byte[] bytes, String charset)
+    {
+        return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
+    }
+
+    /**
+     * 解码字节码
+     * 
+     * @param data 字符串
+     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
+     * @return 解码后的字符串
+     */
+    public static String str(byte[] data, Charset charset)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+
+        if (null == charset)
+        {
+            return new String(data);
+        }
+        return new String(data, charset);
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     * 
+     * @param data 数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, String charset)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+
+        return str(data, Charset.forName(charset));
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     * 
+     * @param data 数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, Charset charset)
+    {
+        if (null == charset)
+        {
+            charset = Charset.defaultCharset();
+        }
+        return charset.decode(data).toString();
+    }
+
+    // ----------------------------------------------------------------------- 全角半角转换
+    /**
+     * 半角转全角
+     * 
+     * @param input String.
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input)
+    {
+        return toSBC(input, null);
+    }
+
+    /**
+     * 半角转全角
+     * 
+     * @param input String
+     * @param notConvertSet 不替换的字符集合
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input, Set<Character> notConvertSet)
+    {
+        char c[] = input.toCharArray();
+        for (int i = 0; i < c.length; i++)
+        {
+            if (null != notConvertSet && notConvertSet.contains(c[i]))
+            {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == ' ')
+            {
+                c[i] = '\u3000';
+            }
+            else if (c[i] < '\177')
+            {
+                c[i] = (char) (c[i] + 65248);
+
+            }
+        }
+        return new String(c);
+    }
+
+    /**
+     * 全角转半角
+     * 
+     * @param input String.
+     * @return 半角字符串
+     */
+    public static String toDBC(String input)
+    {
+        return toDBC(input, null);
+    }
+
+    /**
+     * 替换全角为半角
+     * 
+     * @param text 文本
+     * @param notConvertSet 不替换的字符集合
+     * @return 替换后的字符
+     */
+    public static String toDBC(String text, Set<Character> notConvertSet)
+    {
+        char c[] = text.toCharArray();
+        for (int i = 0; i < c.length; i++)
+        {
+            if (null != notConvertSet && notConvertSet.contains(c[i]))
+            {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == '\u3000')
+            {
+                c[i] = ' ';
+            }
+            else if (c[i] > '\uFF00' && c[i] < '\uFF5F')
+            {
+                c[i] = (char) (c[i] - 65248);
+            }
+        }
+        String returnString = new String(c);
+
+        return returnString;
+    }
+
+    /**
+     * 数字金额大写转换 先写个完整的然后将如零拾替换成零
+     * 
+     * @param n 数字
+     * @return 中文大写数字
+     */
+    public static String digitUppercase(double n)
+    {
+        String[] fraction = { "角", "分" };
+        String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
+        String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } };
+
+        String head = n < 0 ? "负" : "";
+        n = Math.abs(n);
+
+        String s = "";
+        for (int i = 0; i < fraction.length; i++)
+        {
+            s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
+        }
+        if (s.length() < 1)
+        {
+            s = "整";
+        }
+        int integerPart = (int) Math.floor(n);
+
+        for (int i = 0; i < unit[0].length && integerPart > 0; i++)
+        {
+            String p = "";
+            for (int j = 0; j < unit[1].length && n > 0; j++)
+            {
+                p = digit[integerPart % 10] + unit[1][j] + p;
+                integerPart = integerPart / 10;
+            }
+            s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
+        }
+        return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
+    }
+}

+ 69 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/CxSelect.java

@@ -0,0 +1,69 @@
+package com.mangoo.rating.recommend;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * CxSelect树结构实体类
+ * 
+ * @author zheng
+ */
+public class CxSelect implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 数据值字段名称
+     */
+    private String v;
+
+    /**
+     * 数据标题字段名称
+     */
+    private String n;
+
+    /**
+     * 子集数据字段名称
+     */
+    private List<CxSelect> s;
+
+    public CxSelect()
+    {
+    }
+
+    public CxSelect(String v, String n)
+    {
+        this.v = v;
+        this.n = n;
+    }
+
+    public List<CxSelect> getS()
+    {
+        return s;
+    }
+
+    public void setN(String n)
+    {
+        this.n = n;
+    }
+
+    public String getN()
+    {
+        return n;
+    }
+
+    public void setS(List<CxSelect> s)
+    {
+        this.s = s;
+    }
+
+    public String getV()
+    {
+        return v;
+    }
+
+    public void setV(String v)
+    {
+        this.v = v;
+    }
+}

+ 198 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/MsgResult.java

@@ -0,0 +1,198 @@
+package com.mangoo.rating.recommend;
+
+
+import com.mangoo.rating.recommend.utils.StringUtils;
+
+import java.util.HashMap;
+
+/**
+ * 操作消息提醒
+ * 
+ * @author zheng
+ */
+public class MsgResult extends HashMap<String, Object>
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 状态码 */
+    public static final String TYPE_TAG = "type";
+
+    /** 返回内容 */
+    public static final String MSG_TAG = "message";
+
+    /** 数据对象 */
+    public static final String DATA_TAG = "data";
+
+    /**
+     * 状态类型
+     */
+    public enum Type
+    {
+        /** 成功 */
+        SUCCESS(1),
+        /** 警告 */
+        WARN(3),
+        /** 错误 */
+        ERROR(3);
+        private final int value;
+
+        Type(int value)
+        {
+            this.value = value;
+        }
+
+        public int value()
+        {
+            return this.value;
+        }
+    }
+
+    /**
+     * 初始化一个新创建的 MsgResult 对象,使其表示一个空消息。
+     */
+    public MsgResult()
+    {
+    }
+
+    /**
+     * 初始化一个新创建的 MsgResult 对象
+     * 
+     * @param type 状态类型
+     * @param msg 返回内容
+     */
+    public MsgResult(Type type, String msg)
+    {
+        super.put(TYPE_TAG, type.value);
+        super.put(MSG_TAG, msg);
+    }
+
+    /**
+     * 初始化一个新创建的 MsgResult 对象
+     * 
+     * @param type 状态类型
+     * @param msg 返回内容
+     * @param data 数据对象
+     */
+    public MsgResult(Type type, String msg, Object data)
+    {
+        super.put(TYPE_TAG, type.value);
+        super.put(MSG_TAG, msg);
+        if (StringUtils.isNotNull(data))
+        {
+            super.put(DATA_TAG, data);
+        }
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @return 成功消息
+     */
+    public static MsgResult success()
+    {
+        return MsgResult.success("操作成功");
+    }
+
+    /**
+     * 返回成功数据
+     * 
+     * @return 成功消息
+     */
+    public static MsgResult success(Object data)
+    {
+        return MsgResult.success("操作成功", data);
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @param msg 返回内容
+     * @return 成功消息
+     */
+    public static MsgResult success(String msg)
+    {
+        return MsgResult.success(msg, null);
+    }
+
+    /**
+     * 方便链式调用
+     *
+     * @param key 键
+     * @param value 值
+     * @return 数据对象
+     */
+    @Override
+    public MsgResult put(String key, Object value)
+    {
+        super.put(key, value);
+        return this;
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 成功消息
+     */
+    public static MsgResult success(String msg, Object data)
+    {
+        return new MsgResult(Type.SUCCESS, msg, data);
+    }
+
+    /**
+     * 返回警告消息
+     * 
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static MsgResult warn(String msg)
+    {
+        return MsgResult.warn(msg, null);
+    }
+
+    /**
+     * 返回警告消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static MsgResult warn(String msg, Object data)
+    {
+        return new MsgResult(Type.WARN, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @return
+     */
+    public static MsgResult error()
+    {
+        return MsgResult.error("操作失败");
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static MsgResult error(String msg)
+    {
+        return MsgResult.error(msg, null);
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static MsgResult error(String msg, Object data)
+    {
+        return new MsgResult(Type.ERROR, msg, data);
+    }
+}

+ 105 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/Mtree.java

@@ -0,0 +1,105 @@
+package com.mangoo.rating.recommend;
+
+import java.io.Serializable;
+
+public class Mtree implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+    /** 节点ID */
+    private Long id;
+
+    /** 节点父ID */
+    private Long parentnodes;
+
+    /** 节点名称 */
+    private String value;
+
+    /** 节点标题 */
+    private String text;
+
+    private String img;
+    
+    private boolean showcheck=false;
+    /** 是否勾选 */
+    private boolean checkstate = false;
+
+    /** 是否展开 */
+    private boolean isexpand = false;
+    
+    private String hasChildren="false";
+    
+    public void setId(Long id) {
+		this.id = id;
+	}
+
+	public void setParentnodes(Long parentnodes) {
+		this.parentnodes = parentnodes;
+	}
+
+	public void setValue(String value) {
+		this.value = value;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public Long getParentnodes() {
+		return parentnodes;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public String getText() {
+		return text;
+	}
+
+	public String getImg() {
+		return img;
+	}
+
+	public boolean isShowcheck() {
+		return showcheck;
+	}
+
+	public boolean isCheckstate() {
+		return checkstate;
+	}
+
+	public boolean isIsexpand() {
+		return isexpand;
+	}
+
+	public String getHasChildren() {
+		return hasChildren;
+	}
+
+	public void setText(String text) {
+		this.text = text;
+	}
+
+	public void setImg(String img) {
+		this.img = img;
+	}
+
+	public void setShowcheck(boolean showcheck) {
+		this.showcheck = showcheck;
+	}
+
+	public void setCheckstate(boolean checkstate) {
+		this.checkstate = checkstate;
+	}
+
+	public void setIsexpand(boolean isexpand) {
+		this.isexpand = isexpand;
+	}
+
+	public void setHasChildren(String hasChildren) {
+		this.hasChildren = hasChildren;
+	}
+
+	
+
+}

+ 92 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/StrFormatter.java

@@ -0,0 +1,92 @@
+package com.mangoo.rating.recommend;
+
+import com.mangoo.rating.recommend.utils.StringUtils;
+
+/**
+ * 字符串格式化
+ * 
+ * @author zheng
+ */
+public class StrFormatter
+{
+    public static final String EMPTY_JSON = "{}";
+    public static final char C_BACKSLASH = '\\';
+    public static final char C_DELIM_START = '{';
+    public static final char C_DELIM_END = '}';
+
+    /**
+     * 格式化字符串<br>
+     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+     * 例:<br>
+     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+     * 
+     * @param strPattern 字符串模板
+     * @param argArray 参数列表
+     * @return 结果
+     */
+    public static String format(final String strPattern, final Object... argArray)
+    {
+        if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray))
+        {
+            return strPattern;
+        }
+        final int strPatternLength = strPattern.length();
+
+        // 初始化定义好的长度以获得更好的性能
+        StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
+
+        int handledPosition = 0;
+        int delimIndex;// 占位符所在位置
+        for (int argIndex = 0; argIndex < argArray.length; argIndex++)
+        {
+            delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
+            if (delimIndex == -1)
+            {
+                if (handledPosition == 0)
+                {
+                    return strPattern;
+                }
+                else
+                { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
+                    sbuf.append(strPattern, handledPosition, strPatternLength);
+                    return sbuf.toString();
+                }
+            }
+            else
+            {
+                if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH)
+                {
+                    if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH)
+                    {
+                        // 转义符之前还有一个转义符,占位符依旧有效
+                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
+                        sbuf.append(Convert.utf8Str(argArray[argIndex]));
+                        handledPosition = delimIndex + 2;
+                    }
+                    else
+                    {
+                        // 占位符被转义
+                        argIndex--;
+                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
+                        sbuf.append(C_DELIM_START);
+                        handledPosition = delimIndex + 1;
+                    }
+                }
+                else
+                {
+                    // 正常占位符
+                    sbuf.append(strPattern, handledPosition, delimIndex);
+                    sbuf.append(Convert.utf8Str(argArray[argIndex]));
+                    handledPosition = delimIndex + 2;
+                }
+            }
+        }
+        // 加入最后一个占位符后所有的字符
+        sbuf.append(strPattern, handledPosition, strPattern.length());
+
+        return sbuf.toString();
+    }
+}

+ 63 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/TreeEntity.java

@@ -0,0 +1,63 @@
+package com.mangoo.rating.recommend;
+
+/**
+ * Tree基类
+ * 
+ * @author zheng
+ */
+public class TreeEntity extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 父菜单名称 */
+    private String parentName;
+
+    /** 父菜单ID */
+    private Long parentId;
+
+    /** 显示顺序 */
+    private Integer orderNum;
+
+    /** 祖级列表 */
+    private String ancestors;
+
+    public String getParentName()
+    {
+        return parentName;
+    }
+
+    public void setParentName(String parentName)
+    {
+        this.parentName = parentName;
+    }
+
+    public Long getParentId()
+    {
+        return parentId;
+    }
+
+    public void setParentId(Long parentId)
+    {
+        this.parentId = parentId;
+    }
+
+    public Integer getOrderNum()
+    {
+        return orderNum;
+    }
+
+    public void setOrderNum(Integer orderNum)
+    {
+        this.orderNum = orderNum;
+    }
+
+    public String getAncestors()
+    {
+        return ancestors;
+    }
+
+    public void setAncestors(String ancestors)
+    {
+        this.ancestors = ancestors;
+    }
+}

+ 60 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/Ztree.java

@@ -0,0 +1,60 @@
+package com.mangoo.rating.recommend;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * Ztree树结构实体类
+ * 
+ * @author zheng
+ */
+@Data
+@ApiModel(value = "Ztree树结构实体类")
+public class Ztree
+{
+
+    /** 节点ID */
+    @ApiModelProperty(value = "节点ID")
+    private Long id;
+
+    /** 节点父ID */
+    @ApiModelProperty(value = "节点父ID")
+    private Long pId;
+
+    /** 节点名称 */
+    @ApiModelProperty(value = "节点名称")
+    private String name;
+
+    /** 节点标题 */
+    @ApiModelProperty(value = "节点标题")
+    private String title;
+
+    /** 是否勾选 */
+    @ApiModelProperty(value = "是否勾选")
+    private boolean checked = false;
+
+    /** 是否展开 */
+    @ApiModelProperty(value = "是否展开")
+    private boolean open = false;
+
+    /** 是否能勾选 */
+    @ApiModelProperty(value = "是否能勾选")
+    private boolean nocheck = false;
+
+    @ApiModelProperty(value = "是否禁用")
+    private boolean disable = false;
+
+    @ApiModelProperty(value = "是否默认勾选")
+    private boolean defaultCheck = false;
+
+    public Long getpId()
+    {
+        return pId;
+    }
+
+    public void setpId(Long pId)
+    {
+        this.pId = pId;
+    }
+}

+ 13 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/ChatMessage.java

@@ -0,0 +1,13 @@
+package com.mangoo.rating.recommend.ai.model;
+
+import lombok.*;
+import java.util.List;
+
+/** 一条对话消息(统一模型,与厂商无关) */
+@Data @Builder @NoArgsConstructor @AllArgsConstructor
+public class ChatMessage {
+    private Role role;
+    private String content;            // 文本
+    private List<ToolCall> toolCalls;  // 仅 assistant 决定调工具时
+    private String toolCallId;         // 仅 role=TOOL:对应 ToolCall.id
+}

+ 12 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/ChatResult.java

@@ -0,0 +1,12 @@
+package com.mangoo.rating.recommend.ai.model;
+
+import lombok.*;
+import java.util.List;
+
+/** 模型一轮调用的结果 */
+@Data @Builder @NoArgsConstructor @AllArgsConstructor
+public class ChatResult {
+    private String stopReason;       // "tool_use" | "end_turn"
+    private String text;             // 本轮文本
+    private List<ToolCall> toolCalls;// stopReason=tool_use 时
+}

+ 4 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/Role.java

@@ -0,0 +1,4 @@
+package com.mangoo.rating.recommend.ai.model;
+
+/** 对话角色(模型无关) */
+public enum Role { USER, ASSISTANT, SYSTEM, TOOL }

+ 4 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/StreamCallback.java

@@ -0,0 +1,4 @@
+package com.mangoo.rating.recommend.ai.model;
+
+/** 流式文本增量回调 */
+public interface StreamCallback { void onTextDelta(String delta); }

+ 11 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/ToolCall.java

@@ -0,0 +1,11 @@
+package com.mangoo.rating.recommend.ai.model;
+
+import lombok.*;
+
+/** 模型发起的一次工具调用 */
+@Data @Builder @NoArgsConstructor @AllArgsConstructor
+public class ToolCall {
+    private String id;            // 工具调用 id(Claude tool_use.id)
+    private String name;          // 工具名
+    private String argumentsJson; // 入参 JSON 字符串
+}

+ 11 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/ai/model/ToolSpec.java

@@ -0,0 +1,11 @@
+package com.mangoo.rating.recommend.ai.model;
+
+import lombok.*;
+
+/** 给模型的工具声明 */
+@Data @Builder @NoArgsConstructor @AllArgsConstructor
+public class ToolSpec {
+    private String name;
+    private String description;
+    private String inputSchemaJson; // JSON Schema 字符串
+}

+ 26 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/annotation/ApiLimitRule.java

@@ -0,0 +1,26 @@
+package com.mangoo.rating.recommend.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * ip限制
+ *
+ * @author
+ */
+@Target({ ElementType.PARAMETER, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface ApiLimitRule {
+     /**几秒内限制*/
+     int seconds();
+     /**可访问次数*/
+     int limitCount();
+     /**超过次数,锁定*/
+     int lockCount() default 0;
+     /**锁定时间*/
+     int lockTime() default 0;
+     /**类型:common:通用ip和url,user:用户 */
+     String type() default "common";
+     /**提醒消息*/
+     String msg() default "";
+}

+ 40 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/annotation/ApiLog.java

@@ -0,0 +1,40 @@
+package com.mangoo.rating.recommend.annotation;
+
+
+import com.mangoo.rating.recommend.enums.BusinessType;
+import com.mangoo.rating.recommend.enums.OperatorType;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义接口调用日志注解
+ *
+ * @author lsz
+ */
+@Target({ ElementType.PARAMETER, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface ApiLog {
+
+    /**
+     * 模块
+     */
+    public String title() default "";
+
+    /**
+     * 功能
+     */
+    public BusinessType businessType() default BusinessType.OTHER;
+
+    /**
+     * 操作人类别
+     */
+    public OperatorType operatorType() default OperatorType.MOBILE;
+
+    /**
+     * 是否保存请求的参数
+     */
+    public boolean isSaveRequestData() default true;
+
+    String indto() default "";
+}

+ 163 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/annotation/Excel.java

@@ -0,0 +1,163 @@
+package com.mangoo.rating.recommend.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+
+/**
+ * 自定义导出Excel数据注解
+ * 
+ * @author zheng
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Excel
+{
+    /**
+     * 导出时在excel中排序
+     */
+    public int sort() default Integer.MAX_VALUE;
+
+    /**
+     * 导出到Excel中的名字.
+     */
+    public String name() default "";
+
+    /**
+     * 是否必填
+     * @return
+     */
+    public boolean isMust() default false ;
+
+    /**
+     * 日期格式, 如: yyyy-MM-dd
+     */
+    public String dateFormat() default "";
+
+    /**
+     * 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
+     */
+    public String dictType() default "";
+
+    /**
+     * 读取内容转表达式 (如: 0=男,1=女,2=未知)
+     */
+    public String readConverterExp() default "";
+
+    /**
+     * 读取内容方法
+     */
+    public String readFormatDataMethod() default "";
+
+    /**
+     * 分隔符,读取字符串组内容
+     */
+    public String separator() default ",";
+
+    /**
+     * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
+     */
+    public int scale() default -1;
+
+    /**
+     * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
+     */
+    public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
+
+    /**
+     * 导出类型(0数字 1字符串)
+     */
+    public ColumnType cellType() default ColumnType.STRING;
+
+    /**
+     * 导出时在excel中每个列的高度 单位为字符
+     */
+    public double height() default 14;
+
+    /**
+     * 导出时在excel中每个列的宽 单位为字符
+     */
+    public double width() default 16;
+
+    /**
+     * 文字后缀,如% 90 变成90%
+     */
+    public String suffix() default "";
+
+    /**
+     * 当值为空时,字段的默认值
+     */
+    public String defaultValue() default "";
+
+    /**
+     * 提示信息
+     */
+    public String prompt() default "";
+
+    /**
+     * 设置只能选择不能输入的列内容.
+     */
+    public String[] combo() default {};
+
+    /**
+     * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
+     */
+    public boolean isExport() default true;
+
+    /**
+     * 另一个类中的属性名称,支持多级获取,以小数点隔开
+     */
+    public String targetAttr() default "";
+
+    /**
+     * 是否自动统计数据,在最后追加一行统计数据总和
+     */
+    public boolean isStatistics() default false;
+
+    /**
+     * 内容脱敏
+     * 正则
+     * @return
+     */
+    public String desensitize() default "";
+
+    public String desensitizeType() default "";
+    /**
+     * 字段类型(0:导出导入;1:仅导出;2:仅导入)
+     */
+    Type type() default Type.ALL;
+
+    public enum Type
+    {
+        ALL(0), EXPORT(1), IMPORT(2);
+        private final int value;
+
+        Type(int value)
+        {
+            this.value = value;
+        }
+
+        public int value()
+        {
+            return this.value;
+        }
+    }
+
+    public enum ColumnType
+    {
+        NUMERIC(0), STRING(1);
+        private final int value;
+
+        ColumnType(int value)
+        {
+            this.value = value;
+        }
+
+        public int value()
+        {
+            return this.value;
+        }
+    }
+}

+ 33 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/annotation/RequiresPermission.java

@@ -0,0 +1,33 @@
+package com.mangoo.rating.recommend.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 权限校验注解
+ *
+ * @author gengjintao
+ * @date 2026/04/01
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RequiresPermission {
+
+    /**
+     * 权限编码数组
+     */
+    String[] value() default {};
+
+    /**
+     * 逻辑关系:AND-需要全部权限,OR-需要任一权限
+     */
+    Logical logical() default Logical.AND;
+
+    /**
+     * 逻辑关系枚举
+     */
+    enum Logical {
+        AND,
+        OR
+    }
+}

+ 12 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/annotation/ValueAspect.java

@@ -0,0 +1,12 @@
+package com.mangoo.rating.recommend.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.METHOD, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface ValueAspect {
+    String value();
+
+    String defaultValue() default "";
+}

+ 30 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/condition/MoldNumberQueryCondition.java

@@ -0,0 +1,30 @@
+package com.mangoo.rating.recommend.condition;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.mangoo.rating.recommend.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("盒子编号查询请求")
+public class MoldNumberQueryCondition extends BaseEntity {
+
+    @ApiModelProperty(value = "盒子编号")
+    private String moldNumber;
+
+    @ApiModelProperty(value = "盒子状态(0-空闲,1-使用中,2-维修中,3-报废)")
+    private Integer moldStatus;
+
+    @ApiModelProperty(value = "订单编号")
+    private String orderNo;
+
+    //分页
+    @ExcelIgnore
+    @ApiModelProperty(value =  "当前页")
+    private Integer page;
+
+    @ExcelIgnore
+    @ApiModelProperty(value =  "分页大小")
+    private Integer rows;
+}

+ 81 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/condition/OrderCardQueryCondition.java

@@ -0,0 +1,81 @@
+package com.mangoo.rating.recommend.condition;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.mangoo.rating.recommend.BaseEntity;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@Data
+public class OrderCardQueryCondition extends BaseEntity {
+
+    /**
+     * 订单ID
+     */
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long orderId;
+    
+    /**
+     * 订单编号
+     */
+    private String orderNo;
+
+    /**
+     * 卡片状态列表
+     */
+    private List<Integer> cardStatusList;
+
+    /**
+     * 快递单号
+     */
+    private String logisticsNo;
+
+    /**
+     * 操作人类型
+     */
+    @NotBlank(message = "操作人类型不能为空")
+    private String operatorType;
+
+    /**
+     * 最后发货时间
+     */
+    private String lastTimeShipStart;
+    /**
+     * 最后发货时间
+     */
+    private String lastTimeShipEnd;
+
+    /**
+     *  卡片编号
+     */
+    private String cardNo;
+
+
+    /**
+     *  卡片状态(枚举:100 = 未开始,101 = 待寄出,102 = 已寄出,202 = 已入库,203 = 预审未通过,204 = 预审通过,205 = 待送评,301 = 评级中-拍照,302 = 评级中-打分,303 = 评级中-生成报告,304 = 评级中-审核报告,305 = 评级中-审核报告通过,306 = 评级中-审核报告未通过,401 = 打印标签,402 = 封装评级壳,501 = 已发货-已寄出,502 = 完成)
+     */
+    private Integer cardStatus;
+
+    /**
+     *   评级编号
+     */
+    private String evaluateNo;
+
+    /**
+     *  用户ID
+     */
+    @NotNull(message = "用户ID不能为空")
+    private Long userId;
+
+
+    /**
+     *  方案编号/名称
+     */
+    @ApiModelProperty(value = "方案编号/名称")
+    private String schemeNo;
+
+}

+ 47 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/condition/OrderQueryCondition.java

@@ -0,0 +1,47 @@
+package com.mangoo.rating.recommend.condition;
+
+import com.mangoo.rating.recommend.BaseEntity;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class OrderQueryCondition extends BaseEntity {
+
+    /**
+     * 订单ID
+     */
+    private String orderNo;
+
+    /**
+     * 订单状态列表
+     */
+    private List<Integer> orderStatusList;
+
+    /**
+     * 订单状态 (100 - 待支付,101 - 已支付待寄出,102 - 已支付已寄出,201 - 订单超时,202 - 退款中,203 - 用户取消,204 - 退款完成,205 - 退款失败,301 - 预评 - 待预审,302 - 预评 - 预审通过,303 - 预评 - 预审未通过,304 - 送评 - 待送评,305 - 送评 - 待送评未支付,306 - 送评 - 待送评已支付,307 - 送评 - 送评中,401 - 评级中 - 拍照,402 - 评级中 - 打分,403 - 评级中 - 生成报告,404 - 评级中 - 审核报告,405 - 评级中 - 审核报告通过,406 - 评级中 - 审核报告未通过,501 - 打印标签,502 - 封装,503 - 打包发货,504 - 已发货,505 - 订单完成)
+     */
+    private Integer orderStatus;
+
+    /**
+     * 快递单号
+     */
+    private String logisticsNo;
+
+    /**
+     * 操作人类型
+     */
+    private String operatorType;
+
+    /**
+     * 标识:查询拆分/合并订单用
+     */
+    private Boolean isMerged;
+
+    /**
+     *  入库状态(枚举:1-未入库,2-已签收,3-已入库)
+     */
+    private Integer inStatus;
+
+
+}

+ 43 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/condition/OrderQueryPageCondition.java

@@ -0,0 +1,43 @@
+package com.mangoo.rating.recommend.condition;
+
+import com.mangoo.rating.recommend.BaseEntity;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class OrderQueryPageCondition extends BaseEntity {
+
+    /**
+     * 订单ID
+     */
+    private String orderNo;
+
+    /**
+     * 订单状态列表
+     */
+    private List<Integer> orderStatusList;
+
+    /**
+     * 订单状态 (100 - 待支付,101 - 已支付待寄出,102 - 已支付已寄出,201 - 订单超时,202 - 退款中,203 - 用户取消,204 - 退款完成,205 - 退款失败,301 - 预评 - 待预审,302 - 预评 - 预审通过,303 - 预评 - 预审未通过,304 - 送评 - 待送评,305 - 送评 - 待送评未支付,306 - 送评 - 待送评已支付,307 - 送评 - 送评中,401 - 评级中 - 拍照,402 - 评级中 - 打分,403 - 评级中 - 生成报告,404 - 评级中 - 审核报告,405 - 评级中 - 审核报告通过,406 - 评级中 - 审核报告未通过,501 - 打印标签,502 - 封装,503 - 打包发货,504 - 已发货,505 - 订单完成)
+     */
+    private Integer orderStatus;
+
+    /**
+     * 快递单号
+     */
+    private String logisticsNo;
+
+    /**
+     * 操作人类型
+     */
+    private String operatorType;
+
+    /**
+     * 标识:查询拆分/合并订单用
+     */
+    private Boolean isMerged;
+
+
+
+}

+ 38 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/condition/RecognizedInfoQueryCondition.java

@@ -0,0 +1,38 @@
+package com.mangoo.rating.recommend.condition;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class RecognizedInfoQueryCondition {
+
+
+    @ApiModelProperty(value = "卡ID")
+    private Long cardId;
+
+    @ApiModelProperty(value = "订单编号")
+    private String orderNo;
+
+    @ApiModelProperty(value = "球员")
+    private String player;
+
+    @ApiModelProperty(value = "年份")
+    private String year;
+
+    @ApiModelProperty(value = "系列")
+    private String series;
+
+    @ApiModelProperty(value = "卡种")
+    private String cardSet;
+
+    @ApiModelProperty(value = "卡密编号")
+    private String confidentialId;
+
+    @ApiModelProperty(value = "限编")
+    private String limitId;
+
+}

+ 23 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/condition/SysOperLogQueryCondition.java

@@ -0,0 +1,23 @@
+package com.mangoo.rating.recommend.condition;
+
+import com.mangoo.rating.recommend.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("操作日志查询条件")
+public class SysOperLogQueryCondition extends BaseEntity {
+
+    @ApiModelProperty("接口描述")
+    private String title;
+
+    @ApiModelProperty("管理员账号")
+    private String adminAccount;
+
+    @ApiModelProperty("操作状态")
+    private Integer status;
+
+    @ApiModelProperty("请求方式")
+    private String requestMethod;
+}

+ 26 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/condition/SysUserQueryCondition.java

@@ -0,0 +1,26 @@
+package com.mangoo.rating.recommend.condition;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.mangoo.rating.recommend.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("用户查询请求")
+public class SysUserQueryCondition extends BaseEntity {
+
+    @ApiModelProperty("用户名")
+    private String username;
+
+    @ApiModelProperty("状态:0-禁用,1-正常")
+    private Integer status;
+    //分页
+    @ExcelIgnore
+    @ApiModelProperty(value =  "当前页")
+    private Integer page;
+
+    @ExcelIgnore
+    @ApiModelProperty(value =  "分页大小")
+    private Integer rows;
+}

+ 27 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/condition/UserAddressQueryCondition.java

@@ -0,0 +1,27 @@
+package com.mangoo.rating.recommend.condition;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel(value = "用户新增地址请求", description = "用户新增收货地址请求参数")
+public class UserAddressQueryCondition {
+
+    @ApiModelProperty(value = "地址id", required = true, example = "10001")
+    private Long id;
+
+    /**
+     * 用户ID,关联用户表t_user.id
+     */
+    private Long userId;
+
+    /**
+     * 地址类型 NORMAL-普通地址  COLLECT-代收点
+     */
+    private String addressType;
+}

+ 28 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/AiAssistantProperties.java

@@ -0,0 +1,28 @@
+package com.mangoo.rating.recommend.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/** AI 助手配置:网关、模型与运行参数。仿 TencentCosConfig 风格。 */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "ai")
+public class AiAssistantProperties {
+    /** 公司 Claude 网关基址 */
+    private String baseUrl;
+    /** 网关 API Key */
+    private String apiKey;
+    /** 模型 ID */
+    private String model = "claude-opus-4-8";
+    /** 单次回答最大 token */
+    private Integer maxTokens = 4096;
+    /** 会话上下文 TTL(分钟) */
+    private Integer sessionTtlMinutes = 30;
+    /** Agent loop 最大迭代轮数 */
+    private Integer maxIterations = 5;
+    /** 连接超时(毫秒) */
+    private Integer connectTimeoutMs = 10000;
+    /** 读超时(毫秒),流式较长 */
+    private Integer readTimeoutMs = 120000;
+}

+ 59 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/BaseConfig.java

@@ -0,0 +1,59 @@
+package com.mangoo.rating.recommend.config;
+
+import com.mangoo.rating.recommend.config.thread.MyThreadPoolTaskExecutor;
+import lombok.Data;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+@Configuration
+@Data
+public class BaseConfig {
+	@Value("${tzy.app-version:dev}")
+	private String env;
+
+    // 核心线程池大小
+    private int corePoolSize = 100;
+    // 最大可创建的线程数
+    private int maxPoolSize = 300;
+    // 队列最大长度
+    private int queueCapacity = 600;
+    // 线程池维护线程所允许的空闲时间
+    private int keepAliveSeconds = 120;
+
+    @Bean
+    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
+        ThreadPoolTaskExecutor executor = new MyThreadPoolTaskExecutor();
+        executor.setMaxPoolSize(maxPoolSize);
+        executor.setCorePoolSize(corePoolSize);
+        executor.setQueueCapacity(queueCapacity);
+        executor.setKeepAliveSeconds(keepAliveSeconds);
+        // 线程池对拒绝任务(无线程可用)的处理策略
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        executor.setThreadNamePrefix("order-pool-");
+        return executor;
+    }
+
+    /**
+     * 执行周期性或定时任务
+     */
+    @Bean
+    protected ScheduledExecutorService scheduledExecutorService()
+    {
+        return new ScheduledThreadPoolExecutor(corePoolSize,
+                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build())
+        {
+            @Override
+            protected void afterExecute(Runnable r, Throwable t)
+            {
+                super.afterExecute(r, t);
+            }
+        };
+    }
+}

+ 33 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/CommonConfig.java

@@ -0,0 +1,33 @@
+package com.mangoo.rating.recommend.config;
+
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.Resource;
+
+/**
+ * @author by po'yi
+ * @Classname CommonConfig
+ * @Description TODO
+ * @Date 2021/12/16 14:05
+ */
+@Configuration
+public class CommonConfig {
+	public static BaseConfig baseconfig;
+	public static final String ENV_PRO = "prod";
+	public static final String ENV_TEST = "test";
+	public static final String ENV_DEV = "dev";
+
+
+	@Resource
+	public  void setBaseConfig(BaseConfig baseconfig) {
+		CommonConfig.baseconfig = baseconfig;
+	}
+
+	public static boolean isProdEnv(){
+		return ENV_PRO.equals(baseconfig.getEnv());
+	}
+
+	public static boolean isDevEnv(){
+		return ENV_DEV.equals(baseconfig.getEnv());
+	}
+}

+ 32 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/DynamicRedisConfig.java

@@ -0,0 +1,32 @@
+package com.mangoo.rating.recommend.config;
+
+import org.springframework.data.redis.cache.RedisCache;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.cache.RedisCacheWriter;
+import org.springframework.lang.NonNull;
+
+import java.time.Duration;
+
+public class DynamicRedisConfig extends RedisCacheManager {
+
+    public DynamicRedisConfig(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
+        super(cacheWriter, defaultCacheConfiguration);
+    }
+
+    @Override
+    @NonNull
+    protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
+        String cacheName = name;
+        // 解析缓存名称中的 TTL 配置
+        if (name.contains("#")) {
+            String[] parts = name.split("#");
+            cacheName = parts[0];
+            long ttlSeconds = Long.parseLong(parts[1]);
+
+            // 创建带有特定 TTL 的配置
+            cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttlSeconds));
+        }
+        return super.createRedisCache(cacheName, cacheConfig);
+    }
+}

+ 59 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/Knife4jConfig.java

@@ -0,0 +1,59 @@
+package com.mangoo.rating.recommend.config;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
+
+@Configuration
+@EnableSwagger2WebMvc
+@RequiredArgsConstructor
+public class Knife4jConfig implements WebMvcConfigurer {
+
+    private static final String API_BASE_PACKAGE = "com.mangoo";
+    private static final String API_TITLE = "评级APP服务";
+    private static final String API_DESCRIPTION = "描述:珀懿接口";
+    private static final String API_CONTACT_NAME = "gengjintao";
+    private static final String API_VERSION = "1.0.0";
+    private static final String API_TERMS_OF_SERVICE = "Order";
+    private static final String API_LICENSE_URL = "com/mangoo";
+
+    /**
+     * 配置静态资源映射,允许访问Swagger相关资源
+     */
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("/doc.html")
+                .addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/webjars/**")
+                .addResourceLocations("classpath:/META-INF/resources/webjars/");
+    }
+
+    @Bean
+    public Docket createRestApi() {
+        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
+                                                      .select()
+                                                      .apis(RequestHandlerSelectors.basePackage(API_BASE_PACKAGE))
+                                                      .paths(PathSelectors.any())
+                                                      .build();
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder().title(API_TITLE)
+                                   .description(API_DESCRIPTION)
+                                   .contact(new Contact(API_CONTACT_NAME,null,null))
+                                   .version(API_VERSION)
+                                   .build();
+    }
+
+
+}

+ 29 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/LimitRule.java

@@ -0,0 +1,29 @@
+package com.mangoo.rating.recommend.config;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class LimitRule {
+    public static final String COMMON="common";
+    public static final String USER="user";
+    public static final String ACT="act";
+    /**需要token*/
+    public static final String ACT_TOKEN="act_token";
+
+    /**几秒内限制*/
+    private int seconds;
+    /**可访问次数*/
+    private int limitCount;
+    /**锁定次数,limitCount相比取更大*/
+    private int lockCount;
+    /**锁定时间*/
+    private int lockTime;
+
+    public boolean enableLimitLock() {
+        return this.getLockTime() > 0 && this.getLockCount() > 0;
+    }
+}

+ 83 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/MetricsConfig.java

@@ -0,0 +1,83 @@
+package com.mangoo.rating.recommend.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.binder.MeterBinder;
+import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.sql.DataSource;
+
+
+@Configuration
+public class MetricsConfig {
+
+    /**
+     * 配置MeterRegistry,添加公共标签
+     */
+    @Bean
+    public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
+        return registry -> {
+            registry.config().commonTags(
+                    "application", "order"
+            );
+        };
+    }
+
+    /**
+     * 注册自定义指标绑定器
+     */
+    @Bean
+    public MeterBinder customMetricsBinder() {
+        return registry -> {
+            // 这里可以添加自定义的指标绑定器
+            // 例如:数据库连接池指标、JVM指标等
+        };
+    }
+    /**
+     * 注册Druid连接池指标绑定器
+     */
+    @Bean
+    public MeterBinder druidMetricsBinder(DataSource dataSource) {
+        return registry -> {
+            if (dataSource instanceof DruidDataSource) {
+                DruidDataSource druidDataSource = (DruidDataSource) dataSource;
+
+                // 注册Druid连接池指标
+                registry.gauge("druid.connections.active.count",
+                        druidDataSource,
+                        ds -> (double) ds.getActiveCount()
+                );
+
+                registry.gauge("druid.connections.max.active",
+                        druidDataSource,
+                        ds -> (double) ds.getMaxActive()
+                );
+
+                registry.gauge("druid.connections.initial.size",
+                        druidDataSource,
+                        ds -> (double) ds.getInitialSize()
+                );
+
+                registry.gauge("druid.connections.min.idle",
+                        druidDataSource,
+                        ds -> (double) ds.getMinIdle()
+                );
+
+                registry.gauge("druid.connections.max.idle",
+                        druidDataSource,
+                        ds -> (double) ds.getMaxIdle()
+                );
+
+                registry.gauge("druid.connections.max.wait",
+                        druidDataSource,
+                        ds -> (double) ds.getMaxWait()
+                );
+
+            }
+        };
+    }
+
+
+}

+ 60 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/PermissionInterceptorConfig.java

@@ -0,0 +1,60 @@
+package com.mangoo.rating.recommend.config;
+
+import com.mangoo.rating.recommend.interceptor.PermissionInterceptor;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import javax.annotation.Resource;
+
+/**
+ * 权限拦截器配置
+ *
+ * @author gengjintao
+ * @date 2026/04/01
+ */
+@Configuration
+public class PermissionInterceptorConfig implements WebMvcConfigurer {
+
+//    @Resource
+//    private LoginTokenInterceptor loginTokenInterceptor;
+//
+//    @Resource
+//    private DataPermissionInterceptor dataPermissionInterceptor;
+
+    @Resource
+    private PermissionInterceptor permissionInterceptor;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(permissionInterceptor)
+                .addPathPatterns("/**")
+                .excludePathPatterns(
+                        "/swagger-resources/**",
+                        "/webjars/**",
+                        "/v2/**",
+                        "/swagger-ui.html/**",
+                        "/doc.html/**",
+                        "/error",
+                        "/login",
+                        "/logout",
+                        "/captcha/**"
+                );
+
+//        registry.addInterceptor(loginTokenInterceptor).addPathPatterns("/**");
+//
+//        registry.addInterceptor(dataPermissionInterceptor).addPathPatterns("/**");
+//
+//        registry.addInterceptor(permissionInterceptor).addPathPatterns("/**")
+//                .excludePathPatterns(
+//                        "/swagger-resources/**",
+//                        "/webjars/**",
+//                        "/v2/**",
+//                        "/swagger-ui.html/**",
+//                        "/doc.html/**",
+//                        "/error",
+//                        "/login",
+//                        "/logout",
+//                        "/captcha/**");
+    }
+}

+ 343 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/RabbitMQConfig.java

@@ -0,0 +1,343 @@
+package com.mangoo.rating.recommend.config;
+
+import org.springframework.amqp.core.*;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.mangoo.rating.recommend.constant.Constants.ORDER_EXPIRE_15_MINUTES;
+
+/**
+ * RabbitMQ订单超时取消配置类
+ *
+ * @author: gengjintao
+ * @date: 2026/01/21
+ */
+@Configuration
+public class RabbitMQConfig {
+
+
+    // ======================== 订单超时队列 ========================
+    // 订单超时队列名称
+    public static final String ORDER_TIMEOUT_QUEUE = "rating.order.timeout.queue";
+
+    // 订单超时死信队列名称
+    public static final String ORDER_TIMEOUT_DLQ = "rating.order.timeout.dlq";
+
+    // 订单超时交换机名称
+    public static final String ORDER_TIMEOUT_EXCHANGE = "rating.order.timeout.exchange";
+
+    // 订单超时死信交换机名称
+    public static final String ORDER_TIMEOUT_DLX = "rating.order.timeout.dlx";
+
+    // 订单超时路由键
+    public static final String ORDER_TIMEOUT_ROUTING_KEY = "rating.order.timeout.routing.key";
+
+    // 订单超时死信路由键
+    public static final String ORDER_TIMEOUT_DLQ_ROUTING_KEY = "rating.order.timeout.dlq.routing.key";
+
+
+    /**
+     * 订单超时死信交换机
+     */
+    @Bean
+    public DirectExchange orderTimeoutDlx() {
+        return new DirectExchange(ORDER_TIMEOUT_DLX, true, false);
+    }
+
+    /**
+     * 订单超时死信队列
+     */
+    @Bean
+    public Queue orderTimeoutDlq() {
+        return QueueBuilder.durable(ORDER_TIMEOUT_DLQ).build();
+    }
+
+    /**
+     * 死信队列绑定到死信交换机
+     */
+    @Bean
+    public Binding orderTimeoutDlqBinding() {
+        return BindingBuilder.bind(orderTimeoutDlq())
+                .to(orderTimeoutDlx())
+                .with(ORDER_TIMEOUT_DLQ_ROUTING_KEY);
+    }
+
+    /**
+     * 订单超时交换机
+     */
+    @Bean
+    public DirectExchange orderTimeoutExchange() {
+        return new DirectExchange(ORDER_TIMEOUT_EXCHANGE, true, false);
+    }
+
+    /**
+     * 订单超时队列(设置TTL和死信队列)
+     */
+    @Bean
+    public Queue orderTimeoutQueue() {
+        Map<String, Object> args = new HashMap<>();
+        // 设置消息过期时间(毫秒),这里设置为30分钟
+        args.put("x-message-ttl", ORDER_EXPIRE_15_MINUTES);
+        // 设置死信交换机
+        args.put("x-dead-letter-exchange", ORDER_TIMEOUT_DLX);
+        // 设置死信路由键
+        args.put("x-dead-letter-routing-key", ORDER_TIMEOUT_DLQ_ROUTING_KEY);
+
+        return QueueBuilder.durable(ORDER_TIMEOUT_QUEUE)
+                .withArguments(args)
+                .build();
+    }
+
+    /**
+     * 订单超时队列绑定到订单超时交换机
+     */
+    @Bean
+    public Binding orderTimeoutBinding() {
+        return BindingBuilder.bind(orderTimeoutQueue())
+                .to(orderTimeoutExchange())
+                .with(ORDER_TIMEOUT_ROUTING_KEY);
+    }
+
+
+
+
+    // ======================== 订单取消队列 ========================
+
+
+
+    // 订单取消队列名称
+    public static final String ORDER_CANCEL_QUEUE = "rating.order.cancel.queue";
+
+    // 订单取消商品队列名称
+    public static final String ORDER_CANCEL_PRODUCT_QUEUE = "rating.order.cancel.product.queue";
+
+    // 订单取消优惠卷队列名称
+    public static final String ORDER_CANCEL_COUPON_QUEUE = "rating.order.cancel.coupon.queue";
+
+    // 订单取消交换机名称
+    public static final String ORDER_CANCEL_EXCHANGE = "rating.order.cancel.exchange";
+
+    // 订单取消路由键
+    public static final String ORDER_CANCEL_ROUTING_KEY = "rating.order.cancel.routing.key";
+
+    // 订单取消死信交换机名称
+    public static final String ORDER_CANCEL_DLX = "rating.order.cancel.dlx";
+
+    // 订单取消死信队列名称
+    public static final String ORDER_CANCEL_DLQ = "rating.order.cancel.dlq";
+
+    // 订单取消死信路由键
+    public static final String ORDER_CANCEL_DLQ_ROUTING_KEY = "rating.order.cancel.dlq.routing.key";
+
+
+
+    /**
+     * 订单取消交换机
+     */
+    @Bean
+    public DirectExchange orderCancelExchange() {
+        return new DirectExchange(ORDER_CANCEL_EXCHANGE, true, false);
+    }
+
+
+
+    /**
+     * 订单取消死信交换机
+     */
+    @Bean
+    public DirectExchange orderCancelDlx() {
+        return new DirectExchange(ORDER_CANCEL_DLX, true, false);
+    }
+
+    /**
+     * 订单取消死信队列
+     */
+    @Bean
+    public Queue orderCancelDlq() {
+        return QueueBuilder.durable(ORDER_CANCEL_DLQ).build();
+    }
+
+    /**
+     * 订单取消死信队列绑定到死信交换机
+     */
+    @Bean
+    public Binding orderCancelDlqBinding() {
+        return BindingBuilder.bind(orderCancelDlq())
+                .to(orderCancelDlx())
+                .with(ORDER_CANCEL_DLQ_ROUTING_KEY);
+    }
+
+
+    // 商品服务队列
+    @Bean
+    public Queue orderCancelProductQueue() {
+        Map<String, Object> args = new HashMap<>();
+
+        // 设置消息过期时间(毫秒),这里设置为30分钟
+        args.put("x-message-ttl", 30 * 60 * 1000);
+
+        args.put("x-dead-letter-exchange", ORDER_CANCEL_DLX);
+        args.put("x-dead-letter-routing-key", ORDER_CANCEL_ROUTING_KEY);
+
+        return QueueBuilder.durable(ORDER_CANCEL_PRODUCT_QUEUE)
+                .withArguments(args)
+                .build();
+    }
+
+    // 优惠券服务队列
+    @Bean
+    public Queue orderCancelCouponQueue() {
+        Map<String, Object> args = new HashMap<>();
+
+        // 设置消息过期时间(毫秒),这里设置为30分钟
+        args.put("x-message-ttl", 30 * 60 * 1000);
+
+        args.put("x-dead-letter-exchange", ORDER_CANCEL_DLX);
+        args.put("x-dead-letter-routing-key", ORDER_CANCEL_ROUTING_KEY);
+
+        return QueueBuilder.durable(ORDER_CANCEL_COUPON_QUEUE)
+                .withArguments(args)
+                .build();
+    }
+
+    // 取消队列绑定到订单取消交换机(之前错误绑到了orderCreateExchange)
+    @Bean
+    public Binding orderCancelProductBinding() {
+        return BindingBuilder.bind(orderCancelProductQueue())
+                .to(orderCancelExchange())
+                .with(ORDER_CANCEL_ROUTING_KEY);
+    }
+
+    @Bean
+    public Binding orderCancelCouponBinding() {
+        return BindingBuilder.bind(orderCancelCouponQueue())
+                .to(orderCancelExchange())
+                .with(ORDER_CANCEL_ROUTING_KEY);
+    }
+
+
+
+    // ======================== 订单创建队列 ========================
+
+
+
+    // 订单创建交换机名称
+    public static final String ORDER_CREATE_EXCHANGE = "rating.order.create.exchange";
+
+    // 订单创建路由键
+    public static final String ORDER_CREATE_ROUTING_KEY = "rating.order.create.routing.key";
+
+    // 订单创建死信交换机名称
+    public static final String ORDER_CREATE_DLX = "rating.order.create.dlx";
+
+    // 订单创建死信队列名称
+    public static final String ORDER_CREATE_DLQ = "rating.order.create.dlq";
+
+    // 订单创建死信路由键
+    public static final String ORDER_CREATE_DLQ_ROUTING_KEY = "rating.order.create.dlq.routing.key";
+
+    // 订单创建商品服务队列
+    public static final String ORDER_CREATE_PRODUCT_QUEUE = "rating.order.create.product.queue";
+
+    // 订单创建优惠券服务队列
+    public static final String ORDER_CREATE_COUPON_QUEUE = "rating.order.create.coupon.queue";
+
+
+
+
+
+/**        订单创建                 **/
+
+
+    /**
+     * 订单创建交换机
+     */
+    @Bean
+    public DirectExchange orderCreateExchange() {
+        return new DirectExchange(ORDER_CREATE_EXCHANGE, true, false);
+    }
+
+
+    /**
+     * 订单创建死信交换机
+     */
+    @Bean
+    public DirectExchange orderCreateDlx() {
+        return new DirectExchange(ORDER_CREATE_DLX, true, false);
+    }
+
+    /**
+     * 订单创建死信队列
+     */
+    @Bean
+    public Queue orderCreateDlq() {
+        return QueueBuilder.durable(ORDER_CREATE_DLQ).build();
+    }
+
+    /**
+     * 订单创建死信队列绑定到死信交换机
+     */
+    @Bean
+    public Binding orderCreateDlqBinding() {
+        return BindingBuilder.bind(orderCreateDlq())
+                .to(orderCreateDlx())
+                .with(ORDER_CREATE_DLQ_ROUTING_KEY);
+    }
+
+
+
+    // 商品服务队列
+    @Bean
+    public Queue orderCreateProductQueue() {
+        Map<String, Object> args = new HashMap<>();
+
+        // 设置消息过期时间(毫秒),这里设置为30分钟
+        args.put("x-message-ttl", 30 * 60 * 1000);
+
+        args.put("x-dead-letter-exchange", ORDER_CREATE_DLX);
+        args.put("x-dead-letter-routing-key", ORDER_CREATE_ROUTING_KEY);
+
+        return QueueBuilder.durable(ORDER_CREATE_PRODUCT_QUEUE)
+                .withArguments(args)
+                .build();
+    }
+
+    // 优惠券服务队列
+    @Bean
+    public Queue orderCreateCouponQueue() {
+        Map<String, Object> args = new HashMap<>();
+
+        // 设置消息过期时间(毫秒),这里设置为30分钟
+        args.put("x-message-ttl", 30 * 60 * 1000);
+
+        args.put("x-dead-letter-exchange", ORDER_CREATE_DLX);
+        args.put("x-dead-letter-routing-key", ORDER_CREATE_ROUTING_KEY);
+
+        return QueueBuilder.durable(ORDER_CREATE_COUPON_QUEUE)
+                .withArguments(args)
+                .build();
+    }
+
+    // 两个队列都绑定到同一个 Exchange,使用同一个 RoutingKey
+    @Bean
+    public Binding orderCreateProductBinding() {
+        return BindingBuilder.bind(orderCreateProductQueue())
+                .to(orderCreateExchange())
+                .with(ORDER_CREATE_ROUTING_KEY);  // 同一个 routingKey
+    }
+
+    @Bean
+    public Binding orderCreateCouponBinding() {
+        return BindingBuilder.bind(orderCreateCouponQueue())
+                .to(orderCreateExchange())
+                .with(ORDER_CREATE_ROUTING_KEY);  // 同一个 routingKey
+    }
+
+
+
+
+
+}

+ 50 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/RabbitPublisherConfirmConfig.java

@@ -0,0 +1,50 @@
+package com.mangoo.rating.recommend.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.rabbit.connection.CorrelationData;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+
+/**
+ * RabbitMQ 发布确认与退回回调配置
+ * 配合 publisher-confirms: true 和 publisher-returns: true 使用
+ *
+ * @author gengjintao
+ * @date 2026-05-13
+ */
+@Slf4j
+@Configuration
+public class RabbitPublisherConfirmConfig implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
+
+    @Resource
+    private RabbitTemplate rabbitTemplate;
+
+    @PostConstruct
+    public void init() {
+        rabbitTemplate.setConfirmCallback(this);
+        rabbitTemplate.setReturnCallback(this);
+    }
+
+    @Override
+    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
+        if (ack) {
+            log.debug("[发布确认] 消息发送成功,CorrelationId:{}", correlationData != null ? correlationData.getId() : "null");
+        } else {
+            log.error("[发布确认] 消息发送失败,CorrelationId:{},原因:{}", correlationData != null ? correlationData.getId() : "null", cause);
+        }
+    }
+
+    @Override
+    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
+        log.error("[消息退回] 消息无法路由,交换机:{},路由键:{},回复码:{},回复文本:{},消息体:{}",
+                exchange,
+                routingKey,
+                replyCode,
+                replyText,
+                new String(message.getBody()));
+    }
+}

+ 289 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/RedisConfig.java

@@ -0,0 +1,289 @@
+package com.mangoo.rating.recommend.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.lettuce.core.ClientOptions;
+import io.lettuce.core.ReadFrom;
+import org.apache.commons.lang3.StringUtils;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.SentinelServersConfig;
+import org.redisson.config.SingleServerConfig;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheWriter;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.RedisNode;
+import org.springframework.data.redis.connection.RedisPassword;
+import org.springframework.data.redis.connection.RedisSentinelConfiguration;
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.core.*;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.io.IOException;
+import java.time.Duration;
+
+/**
+ * Redis配置
+ *
+ * @author zheng
+ */
+@Configuration
+@EnableCaching
+public class RedisConfig {
+    @Value("${spring.redis.host:127.0.0.1}")
+    private String host;
+    @Value("${spring.redis.port:6379}")
+    private String port;
+    @Value("${spring.redis.password}")
+    private String password;
+    @Value("${spring.redis.sentinel.nodes:127.0.0.1:6379}")
+    private String nodes;
+    @Value("${spring.redis.sentinel.master:master}")
+    private String master;
+
+	@Bean
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory)
+    {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
+        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
+        // 开启事务
+        //redisTemplate.setEnableTransactionSupport(true);
+        redisTemplate.setConnectionFactory(factory);
+        return redisTemplate;
+
+    }
+    @Bean
+    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate)
+    {
+        return redisTemplate.opsForHash();
+    }
+
+    @Bean
+    public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate)
+    {
+        return redisTemplate.opsForValue();
+    }
+
+    @Bean
+    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate)
+    {
+        return redisTemplate.opsForList();
+    }
+
+    @Bean
+    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate)
+    {
+        return redisTemplate.opsForSet();
+    }
+
+    @Bean
+    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate)
+    {
+        return redisTemplate.opsForZSet();
+    }
+
+    @Bean(name = "redissonClient")
+    public RedissonClient redissonClientSingle(RedisProperties redisProperties) throws IOException {
+        Config config = new Config();
+        int timeout = resolveTimeoutMillis(redisProperties);
+        int connectTimeout = Math.max(timeout, 10000);
+
+        if (!"127.0.0.1:6379".equals(nodes) && !"master".equals(master)) {
+            SentinelServersConfig sentinelServers = config.useSentinelServers();
+            sentinelServers.addSentinelAddress("redis://"+nodes).setMasterName(master);
+            if(!StringUtils.isEmpty(password)){
+                sentinelServers.setPassword(password);
+            }
+            // 单次Redis命令响应超时(毫秒),超过后触发重试;不能大于业务tryLock的waitTime
+            sentinelServers.setTimeout(timeout);
+            // 命令执行失败重试次数,timeout触发重试最多3次
+            sentinelServers.setRetryAttempts(3);
+            // 命令重试间隔(毫秒)
+            sentinelServers.setRetryInterval(1500);
+            // TCP连接超时(毫秒)
+            sentinelServers.setConnectTimeout(connectTimeout);
+            // 连接池大小
+            sentinelServers.setMasterConnectionPoolSize(64);
+            sentinelServers.setSlaveConnectionPoolSize(64);
+            // 连接池最小空闲连接数
+            sentinelServers.setMasterConnectionMinimumIdleSize(10);
+            sentinelServers.setSlaveConnectionMinimumIdleSize(10);
+            // 空闲连接超时(毫秒),超时未使用则释放
+            sentinelServers.setIdleConnectionTimeout(10000);
+            // ping检测连接可用性间隔(毫秒),0不检测
+            sentinelServers.setPingConnectionInterval(30000);
+        } else {
+            SingleServerConfig singleServer = config.useSingleServer();
+            singleServer.setAddress("redis://"+host+":"+port);
+            if(!StringUtils.isEmpty(password)){
+                singleServer.setPassword(password);
+            }
+            // 单次Redis命令响应超时(毫秒),超过后触发重试;不能大于业务tryLock的waitTime
+            singleServer.setTimeout(timeout);
+            // 命令执行失败重试次数,timeout触发重试最多3次
+            singleServer.setRetryAttempts(3);
+            // 命令重试间隔(毫秒)
+            singleServer.setRetryInterval(1500);
+            // TCP连接超时(毫秒)
+            singleServer.setConnectTimeout(connectTimeout);
+            // 连接池大小
+            singleServer.setConnectionPoolSize(64);
+            // 连接池最小空闲连接数
+            singleServer.setConnectionMinimumIdleSize(10);
+            // 空闲连接超时(毫秒),超时未使用则释放
+            singleServer.setIdleConnectionTimeout(10000);
+            // ping检测连接可用性间隔(毫秒),0不检测
+            singleServer.setPingConnectionInterval(30000);
+            // 订阅连接池大小
+            singleServer.setSubscriptionConnectionPoolSize(10);
+            // 订阅连接池最小空闲连接数
+            singleServer.setSubscriptionConnectionMinimumIdleSize(1);
+        }
+
+        return Redisson.create(config);
+    }
+
+    private int resolveTimeoutMillis(RedisProperties redisProperties) {
+        Duration timeout = redisProperties.getTimeout();
+        if (timeout == null || timeout.isZero() || timeout.isNegative()) {
+            return 10000;
+        }
+        long timeoutMillis = timeout.toMillis();
+        return (int) Math.max(timeoutMillis, 1000L);
+    }
+
+    @Bean
+    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
+        ObjectMapper om = new ObjectMapper();
+        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
+        // 解决查询缓存转换异常的问题
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
+        jackson2JsonRedisSerializer.setObjectMapper(om);
+        // 配置序列化(解决乱码的问题)
+        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
+                .entryTtl(Duration.ofMillis(-1))
+                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
+                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
+                //.disableCachingNullValues();
+        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
+
+        /**
+         * 一般缓存不需要特别设置
+         * 限制 name#600 注解缓存时间
+         */
+        return new DynamicRedisConfig(cacheWriter, config);
+    }
+
+    /**
+     * 自定义Lettuce连接工厂
+     *
+     * 为什么必须直接创建Bean,不能用BeanPostProcessor修改Spring Boot自动创建的工厂?
+     * 因为BeanPostProcessor.postProcessAfterInitialization()在afterPropertiesSet()之后执行,
+     * 此时连接已按默认的shareNativeConnection=true初始化完毕,修改属性为时已晚。
+     * 而@Bean方法中设置的属性会在Spring调用afterPropertiesSet()之前生效,因此必须用这种方式。
+     *
+     * 关键配置:
+     * 1. shareNativeConnection=false:不共享单一连接,使用连接池,单根连接断了不影响其他请求
+     * 2. validateConnection=true:取连接前PING检测有效性,剔除已被Redis断开的死连接
+     */
+    @Bean(destroyMethod = "destroy")
+    public LettuceConnectionFactory lettuceConnectionFactory(RedisProperties redisProperties) {
+        // 构建Lettuce客户端配置(有连接池配置时使用支持连接池的builder)
+        RedisProperties.Pool pool = redisProperties.getLettuce() != null
+                ? redisProperties.getLettuce().getPool() : null;
+        LettuceClientConfiguration clientConfig;
+
+        if (pool != null) {
+            // 有连接池配置,使用LettucePoolingClientConfiguration.builder()
+            GenericObjectPoolConfig<?> poolConfig = new GenericObjectPoolConfig<>();
+            poolConfig.setMaxTotal(pool.getMaxActive());
+            poolConfig.setMaxIdle(pool.getMaxIdle());
+            poolConfig.setMinIdle(pool.getMinIdle());
+            if (pool.getMaxWait() != null) {
+                poolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
+            }
+            LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder poolBuilder =
+                    LettucePoolingClientConfiguration.builder();
+            poolBuilder.poolConfig(poolConfig);
+            // 客户端选项:自动重连 + 断连时拒绝命令
+            poolBuilder.clientOptions(ClientOptions.builder()
+                    .autoReconnect(true)
+                    .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
+                    .build());
+            // 命令超时
+            if (redisProperties.getTimeout() != null) {
+                poolBuilder.commandTimeout(redisProperties.getTimeout());
+            }
+            // 哨兵模式读写分离
+            if (!"127.0.0.1:6379".equals(nodes) && !"master".equals(master)) {
+                poolBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
+            }
+            clientConfig = poolBuilder.build();
+        } else {
+            // 无连接池配置
+            LettuceClientConfiguration.LettuceClientConfigurationBuilder builder =
+                    LettuceClientConfiguration.builder();
+            builder.clientOptions(ClientOptions.builder()
+                    .autoReconnect(true)
+                    .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
+                    .build());
+            if (redisProperties.getTimeout() != null) {
+                builder.commandTimeout(redisProperties.getTimeout());
+            }
+            if (!"127.0.0.1:6379".equals(nodes) && !"master".equals(master)) {
+                builder.readFrom(ReadFrom.REPLICA_PREFERRED);
+            }
+            clientConfig = builder.build();
+        }
+
+        // 创建连接工厂:根据是否为哨兵模式选择不同配置
+        LettuceConnectionFactory factory;
+        if (!"127.0.0.1:6379".equals(nodes) && !"master".equals(master)) {
+            // 哨兵模式
+            RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration();
+            sentinelConfig.setMaster(master);
+            for (String node : nodes.split(",")) {
+                String[] parts = node.trim().split(":");
+                sentinelConfig.addSentinel(new RedisNode(parts[0], Integer.parseInt(parts[1])));
+            }
+            sentinelConfig.setPassword(RedisPassword.of(redisProperties.getPassword()));
+            sentinelConfig.setDatabase(redisProperties.getDatabase());
+            factory = new LettuceConnectionFactory(sentinelConfig, clientConfig);
+        } else {
+            // 单机模式
+            RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration();
+            standaloneConfig.setHostName(redisProperties.getHost());
+            standaloneConfig.setPort(redisProperties.getPort());
+            standaloneConfig.setPassword(RedisPassword.of(redisProperties.getPassword()));
+            standaloneConfig.setDatabase(redisProperties.getDatabase());
+
+            factory = new LettuceConnectionFactory(standaloneConfig, clientConfig);
+        }
+
+        // 关键:在afterPropertiesSet()之前设置,确保连接池模式生效
+        // @Bean方法中设置的属性会在Spring调用afterPropertiesSet()之前生效
+        factory.setShareNativeConnection(false);
+        factory.setValidateConnection(true);
+
+        return factory;
+    }
+}

+ 88 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/ServerModel.java

@@ -0,0 +1,88 @@
+package com.mangoo.rating.recommend.config;
+
+import lombok.Data;
+import lombok.ToString;
+
+@Data
+@ToString
+public class ServerModel {
+    private String custNo; //公司编号
+    private String custName; //公司名称
+    private String systemType;
+    private String dbType;
+    private String serverName;//服务器地址
+    private String dbName; //数据库名称
+    private String dbUser;
+    private String dbPassword;
+    private String dbDriver;
+    private String dbPort;
+    private String status;
+	public String getCustNo() {
+		return custNo;
+	}
+	public void setCustNo(String custNo) {
+		this.custNo = custNo;
+	}
+	public String getCustName() {
+		return custName;
+	}
+	public void setCustName(String custName) {
+		this.custName = custName;
+	}
+	public String getSystemType() {
+		return systemType;
+	}
+	public void setSystemType(String systemType) {
+		this.systemType = systemType;
+	}
+	public String getDbType() {
+		return dbType;
+	}
+	public void setDbType(String dbType) {
+		this.dbType = dbType;
+	}
+	public String getServerName() {
+		return serverName;
+	}
+	public void setServerName(String serverName) {
+		this.serverName = serverName;
+	}
+	public String getDbName() {
+		return dbName;
+	}
+	public void setDbName(String dbName) {
+		this.dbName = dbName;
+	}
+	public String getDbUser() {
+		return dbUser;
+	}
+	public void setDbUser(String dbUser) {
+		this.dbUser = dbUser;
+	}
+	public String getDbPassword() {
+		return dbPassword;
+	}
+	public void setDbPassword(String dbPassword) {
+		this.dbPassword = dbPassword;
+	}
+	public String getDbDriver() {
+		return dbDriver;
+	}
+	public void setDbDriver(String dbDriver) {
+		this.dbDriver = dbDriver;
+	}
+	public String getDbPort() {
+		return dbPort;
+	}
+	public void setDbPort(String dbPort) {
+		this.dbPort = dbPort;
+	}
+	public String getStatus() {
+		return status;
+	}
+	public void setStatus(String status) {
+		this.status = status;
+	}
+
+
+}

+ 57 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/TencentCosConfig.java

@@ -0,0 +1,57 @@
+package com.mangoo.rating.recommend.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 腾讯云OSS配置
+ *
+ * @author: gengjintao
+ * @date: 2026/01/26
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "tencent.cos")
+public class TencentCosConfig {
+
+    /**
+     * 腾讯云SecretId
+     */
+    private String secretId;
+
+    /**
+     * 腾讯云SecretKey
+     */
+    private String secretKey;
+
+    /**
+     * 所属地域
+     */
+    private String region;
+
+    /**
+     * 存储桶名称
+     */
+    private String bucketName;
+
+    /**
+     * 访问域名
+     */
+    private String domain;
+
+    /**
+     * 文件路径前缀
+     */
+    private String prefix;
+
+    /**
+     * 文件大小限制(默认10MB)
+     */
+    private Long maxFileSize = 10 * 1024 * 1024L;
+
+    /**
+     * 允许上传的文件类型
+     */
+    private String[] allowFileTypes = {"jpg", "jpeg", "png", "gif", "bmp", "webp"};
+}

+ 32 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/MyThreadPoolTaskExecutor.java

@@ -0,0 +1,32 @@
+package com.mangoo.rating.recommend.config.thread;
+
+import org.slf4j.MDC;
+import org.springframework.lang.NonNull;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+public final class MyThreadPoolTaskExecutor  extends ThreadPoolTaskExecutor {
+    public MyThreadPoolTaskExecutor() {
+        super();
+    }
+    
+    @Override
+    public void execute(@NonNull Runnable task) {
+        super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
+    }
+ 
+
+    @NonNull
+    @Override
+    public <T> Future<T> submit(@NonNull Callable<T> task) {
+        return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
+    }
+
+    @NonNull
+    @Override
+    public Future<?> submit(@NonNull Runnable task) {
+        return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
+    }
+}

+ 51 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/RejectedPolicyExecution.java

@@ -0,0 +1,51 @@
+package com.mangoo.rating.recommend.config.thread;
+
+import lombok.extern.log4j.Log4j2;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *  自定义拒绝策略
+ *  @author: gengjintao
+ *  @date: 2026/02/06
+ */
+@Component
+@Log4j2
+public class RejectedPolicyExecution implements RejectedExecutionHandler {
+    
+        private String bizName; // 业务标识(订单/商品/定时)
+
+        // 无参构造(Spring实例化用)
+        public RejectedPolicyExecution() {}
+
+        // 带业务标识构造(配置类创建线程池时设置)
+        public RejectedPolicyExecution(String bizName) {
+            this.bizName = bizName;
+        }
+
+        @Override
+        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
+            if (executor.isShutdown()) {
+                return;
+            }
+            // 打印拒绝日志(核心:记录线程池状态,线上可加报警)
+            log.info("【{}线程池】任务被拒绝!活跃线程:{},队列任务:{},总任务:{}",
+                    bizName, executor.getActiveCount(), executor.getQueue().size(), executor.getTaskCount());
+            // 调用者线程执行任务(CallerRunsPolicy语义:不重复放入队列,直接在调用者线程执行)
+            try {
+                r.run();
+            } catch (Exception e) {
+                log.info("【{}线程池】调用者执行拒绝任务异常:{}", bizName, e.getMessage());
+            }
+        }
+
+        // 设置业务标识(配置类调用)
+        public void setBizName(String bizName) {
+            this.bizName = bizName;
+        }
+        
+}

+ 53 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/ThreadFactoryConfig.java

@@ -0,0 +1,53 @@
+package com.mangoo.rating.recommend.config.thread;
+
+import lombok.extern.log4j.Log4j2;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 自定义线程工厂(线程命名化)
+ *
+ *  @author: gengjintao
+ *  @date: 2026/02/06
+ */
+@Component
+@Log4j2
+public class ThreadFactoryConfig  implements ThreadFactory {
+
+        // 线程序号原子计数器(每个业务线程池独立计数,保证名称唯一)
+        private final ThreadLocal<AtomicInteger> threadSeq = ThreadLocal.withInitial(() -> new AtomicInteger(1));
+        private String threadNamePrefix; // 业务线程前缀
+        private final boolean daemon; // 是否为守护线程,默认false
+
+        // 无参构造(Spring实例化用)
+        public ThreadFactoryConfig() {
+            this.daemon = false;
+        }
+
+        // 带前缀构造(配置类创建线程池时设置业务前缀)
+        public ThreadFactoryConfig(String threadNamePrefix) {
+            this.threadNamePrefix = threadNamePrefix;
+            this.daemon = false;
+        }
+
+        @Override
+        public Thread newThread(Runnable r) {
+            Thread thread = new Thread(r, threadNamePrefix + threadSeq.get().getAndIncrement());
+            thread.setDaemon(daemon);
+            thread.setPriority(Thread.NORM_PRIORITY);
+            // 捕获未处理异常,避免线程池线程销毁(实际项目替换为logback/log4j2)
+            thread.setUncaughtExceptionHandler((t, e) -> {
+                log.info("【{}】线程执行异常,线程名:{},异常:{}",
+                        threadNamePrefix.replace("-thread-", ""), t.getName(), e.getMessage());
+            });
+            return thread;
+        }
+
+        // 设置线程名前缀(配置类调用)
+        public void setThreadNamePrefix(String threadNamePrefix) {
+            this.threadNamePrefix = threadNamePrefix;
+        }
+
+}

+ 69 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/ThreadMdcUtil.java

@@ -0,0 +1,69 @@
+package com.mangoo.rating.recommend.config.thread;
+
+import org.slf4j.MDC;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+
+public final class ThreadMdcUtil {
+    private static final String TRACE_ID = "TRACE_ID";
+ 
+    // 获取唯一性标识
+    public static String generateTraceId() {
+        return UUID.randomUUID().toString();
+    }
+ 
+    public static void setTraceIdIfAbsent() {
+        if (MDC.get(TRACE_ID) == null) {
+            MDC.put(TRACE_ID, generateTraceId());
+        }
+    }
+ 
+    /**
+     * 用于父线程向线程池中提交任务时,将自身MDC中的数据复制给子线程
+     *
+     * @param callable
+     * @param context
+     * @param <T>
+     * @return
+     */
+    public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
+        return () -> {
+            if (context == null) {
+                MDC.clear();
+            } else {
+                MDC.setContextMap(context);
+            }
+            setTraceIdIfAbsent();
+            try {
+                return callable.call();
+            } finally {
+                MDC.clear();
+            }
+        };
+    }
+ 
+    /**
+     * 用于父线程向线程池中提交任务时,将自身MDC中的数据复制给子线程
+     *
+     * @param runnable
+     * @param context
+     * @return
+     */
+    public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
+        return () -> {
+            if (context == null) {
+                MDC.clear();
+            } else {
+                MDC.setContextMap(context);
+            }
+            setTraceIdIfAbsent();
+            try {
+                runnable.run();
+            } finally {
+                MDC.clear();
+            }
+        };
+    }
+}

+ 58 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/ThreadPoolBeanConfig.java

@@ -0,0 +1,58 @@
+package com.mangoo.rating.recommend.config.thread;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 线程池Spring配置类
+ *
+ *  @author: gengjintao
+ *  @date: 2026/02/06
+ */
+@Configuration
+public class ThreadPoolBeanConfig {
+
+    // ===================== 1. 订单业务线程池 Bean(名称:orderExecutorService) =====================
+    @Bean(ThreadPoolConfig.ORDER_THREAD_BEAN_NAME)
+    public ThreadPoolExecutor orderExecutorService(ThreadFactoryConfig threadFactory,
+                                                   RejectedPolicyExecution rejectedPolicy) {
+        // 设置线程工厂的业务前缀
+        threadFactory.setThreadNamePrefix(ThreadPoolConfig.ORDER_THREAD_PREFIX);
+        // 设置拒绝策略的业务标识
+        rejectedPolicy.setBizName("订单线程池");
+        // 有界工作队列(避免OOM)
+        LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(ThreadPoolConfig.ORDER_QUEUE);
+        // 手动创建ThreadPoolExecutor(禁止使用Executors)
+        return new ThreadPoolExecutor(
+                ThreadPoolConfig.ORDER_CORE,
+                ThreadPoolConfig.ORDER_MAX,
+                ThreadPoolConfig.KEEP_ALIVE_TIME,
+                ThreadPoolConfig.DEFAULT_TIME_UNIT,
+                workQueue,
+                threadFactory,
+                rejectedPolicy
+        );
+    }
+
+    // ===================== 2. 通用业务线程池 Bean(名称:goodsExecutorService) =====================
+    @Bean(ThreadPoolConfig.GOODS_THREAD_BEAN_NAME)
+    public ThreadPoolExecutor goodsExecutorService(ThreadFactoryConfig threadFactory,
+                                                   RejectedPolicyExecution rejectedPolicy) {
+        threadFactory.setThreadNamePrefix(ThreadPoolConfig.GOODS_THREAD_PREFIX);
+        rejectedPolicy.setBizName("通用线程池");
+        LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(ThreadPoolConfig.GOODS_QUEUE);
+        return new ThreadPoolExecutor(
+                ThreadPoolConfig.GOODS_CORE,
+                ThreadPoolConfig.GOODS_MAX,
+                ThreadPoolConfig.KEEP_ALIVE_TIME,
+                ThreadPoolConfig.DEFAULT_TIME_UNIT,
+                workQueue,
+                threadFactory,
+                rejectedPolicy
+        );
+    }
+
+}

+ 68 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/ThreadPoolBeanShutdownHandler.java

@@ -0,0 +1,68 @@
+package com.mangoo.rating.recommend.config.thread;
+
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import static com.mangoo.rating.recommend.config.thread.ThreadPoolConfig.*;
+
+/**
+ * Spring线程池优雅关闭处理器(容器销毁前执行,自动关闭所有线程池Bean)
+ *
+ *  @author: gengjintao
+ *  @date: 2026/02/06
+ */
+@Component
+@Log4j2
+public class ThreadPoolBeanShutdownHandler{
+
+
+    // 注入所有线程池Bean
+    @Resource
+    @Qualifier(ORDER_THREAD_BEAN_NAME)
+    private ThreadPoolExecutor orderExecutorService;
+
+    @Resource
+    @Qualifier(GOODS_THREAD_BEAN_NAME)
+    private ThreadPoolExecutor goodsExecutorService;
+
+
+
+    /**
+     * @PreDestroy:Spring容器销毁前触发,优先级高于JVM关闭钩子
+     */
+    @PreDestroy
+    public void shutdownAllThreadPool() {
+        // 关闭普通业务线程池
+        shutdownThreadPool(orderExecutorService, "订单线程池");
+        shutdownThreadPool(goodsExecutorService, "通用线程池");
+        log.info("============线程池Bean已优雅关闭===========");
+    }
+
+    // 优雅关闭普通ThreadPoolExecutor
+    private void shutdownThreadPool(ThreadPoolExecutor pool, String bizName) {
+        if (pool == null || pool.isShutdown()) {
+            log.info("【{}】线程池已关闭,无需重复操作%n", bizName);
+            return;
+        }
+        try {
+            pool.shutdown();
+            if (!pool.awaitTermination(SHUTDOWN_TIMEOUT, DEFAULT_TIME_UNIT)) {
+                log.info("【{}】线程池等待超时,强制关闭%n", bizName);
+                pool.shutdownNow();
+                if (!pool.awaitTermination(SHUTDOWN_TIMEOUT, DEFAULT_TIME_UNIT)) {
+                    log.info("【{}】线程池强制关闭失败%n", bizName);
+                }
+            }
+            log.info("【{}】线程池优雅关闭成功,完成任务:%d%n", bizName, pool.getCompletedTaskCount());
+        } catch (InterruptedException e) {
+            pool.shutdownNow();
+            Thread.currentThread().interrupt();
+            log.info("【{}】线程池关闭被中断,强制关闭%n", bizName);
+        }
+    }
+}

+ 35 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/config/thread/ThreadPoolConfig.java

@@ -0,0 +1,35 @@
+package com.mangoo.rating.recommend.config.thread;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 线程池常量管理
+ *
+ * @author: gengjintao
+ * @date: 2026/02/06
+ */
+public class ThreadPoolConfig {
+
+    // 基础配置:CPU核心数
+    public static final int CPU_CORE_NUM = Runtime.getRuntime().availableProcessors();
+    public static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
+    // 空闲线程存活时间(非核心线程)
+    public static final long KEEP_ALIVE_TIME = 60L;
+    // 优雅关闭超时时间
+    public static final long SHUTDOWN_TIMEOUT = 30L;
+
+    // ===================== 订单业务线程池(IO密集:下单/支付/退款) =====================
+    public static final String ORDER_THREAD_BEAN_NAME = "orderExecutorService";
+    public static final String ORDER_THREAD_PREFIX = "order-biz-thread-";
+    public static final int ORDER_CORE = CPU_CORE_NUM * 2;
+    public static final int ORDER_MAX = CPU_CORE_NUM * 4;
+    public static final int ORDER_QUEUE = 2048;
+
+    // ===================== 通用业务线程池(混合密集:库存更新/上下架) =====================
+    public static final String GOODS_THREAD_BEAN_NAME = "commonExecutorService";
+    public static final String GOODS_THREAD_PREFIX = "common-biz-thread-";
+    public static final int GOODS_CORE = CPU_CORE_NUM * 3;
+    public static final int GOODS_MAX = CPU_CORE_NUM * 5;
+    public static final int GOODS_QUEUE = 1024;
+
+}

+ 53 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/constant/ActConstans.java

@@ -0,0 +1,53 @@
+package com.mangoo.rating.recommend.constant;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 广告常量
+ */
+public interface ActConstans {
+    /**拼团-生态购*/
+    String GROUP_SALE_CODE = "group_sale_code";
+    /**拼团-首购金*/
+    String GROUP_FIRST_ORDER = "group_first_order";
+    /**拼团-商家赠礼*/
+    String GROUP_MERCHANT_GIFT = "merchant_act_send_gift";
+    /**拼团-随机卡密优惠劵*/
+    String GROUP_COUPON_RANDOM_GOODS = "group_coupon_random_goods";
+    /**拼团-商家指定卡密优惠劵*/
+    String GROUP_COUPON_SELECT_GOODS = "group_coupon_select_goods";
+    /**拼团活动记录*/
+    String GROUP_ACT_RECORD = "group_act_record";
+
+    /**拼团-免单*/
+    String GROUP_FREE_ORDER = "group_free_order";
+    /** 拼团组齐后发放 */
+    String MERCHANT_ACT_GROUP_FULL="merchant_act_group_full";
+    /** 公示卡密后发放 */
+    String MERCHANT_ACT_PUBLIC_GOODS="merchant_act_public_goods";
+    /** 订单购买后分配 */
+    String MERCHANT_ACT_AFT_ORDER="merchant_act_aft_order";
+    /**平台优惠劵*/
+    String COUPON_COMMON="coupon_common";
+    /**商家优惠劵*/
+    String COUPON_MERCHANT="coupon_merchant";
+    /** 拼团卡密活动查询传null时默认查询类型,组齐或购买后卡密活动 */
+    List<String> NOT_NULL_ACT_TYPE = Arrays.asList(MERCHANT_ACT_PUBLIC_GOODS, MERCHANT_ACT_GROUP_FULL, MERCHANT_ACT_AFT_ORDER);
+    /** 允许克隆的拼团活动类型 */
+    List<String> NEED_CLONE_ACT_TYPE = Arrays.asList(GROUP_COUPON_RANDOM_GOODS, GROUP_COUPON_SELECT_GOODS, GROUP_FIRST_ORDER);
+    /** 优惠劵类型 */
+    List<String> DISCOUNT_COUPON_TYPES = Arrays.asList(COUPON_COMMON, COUPON_MERCHANT);
+    /**满减优惠劵*/
+    String COUPON_FULL_DISCOUNT="full_discount";
+    /**折扣优惠劵*/
+    String COUPON_RATE_DISCOUNT="rete_discount";
+    /**代金劵,直接抵扣*/
+    String COUPON_VOUCHER="voucher";
+    /**盒*/
+    String BOX="BOX";
+    /**箱*/
+    String CARTON="CARTON";
+    /**包*/
+    String PACK="PACK";
+}

+ 27 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/constant/AdConstans.java

@@ -0,0 +1,27 @@
+package com.mangoo.rating.recommend.constant;
+
+/**
+ * 广告常量
+ */
+public interface AdConstans {
+    /**广告位置,默认首页,home,市场页:market,搜索:search*/
+    String AD_LOCATION_HOME="home";
+    String AD_LOCATION_SEARCH="search";
+    String AD_LOCATION_MARKET="market";
+    /** 位置类型,home_hot,basketball,Soccer,other,综合收藏,group,search,推荐:recommend */
+    String LOCATION_TYPE_HOME_HOT="home_hot";
+    String LOCATION_TYPE_RECOMMEND="recommend";
+    /**投放标签,默认all全部*/
+    String AD_LABEL_ALL="all";
+    String SPORT_BASKETBALL="basketball";
+    String SPORT_SOCCER="soccer";
+    String SPORT_OTHER="other";
+    String SPORT_GROUP="综合收藏";
+    /**会员等级前缀*/
+    String MEMBER_LEVEL_PRE="member_level_";
+    /**会员等级:钻石会员*/
+    String MEMBER_LEVEL_3="member_level_3";
+    /**会员等级:黑钻会员*/
+    String MEMBER_LEVEL_4="member_level_4";
+
+}

+ 29 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/constant/BusConstants.java

@@ -0,0 +1,29 @@
+package com.mangoo.rating.recommend.constant;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 业务相关敞亮
+ * @author: zhenhua.bian
+ * @date: 2025/10/24
+ */
+public class BusConstants {
+    /**
+     * 七牛云token
+     */
+    public static String QI_NIU_TOKEN = "qiniu_token";
+    public static final long QI_NIU_TOKEN_EXPIRE_TIME = 1800;
+    public static final String bucketName = "tzy-admin-public";
+    public static final String accessKey = "RYT6zfQA5Yf4IKgbaDBPMjEgQmzKdcfN48M8VIvI";
+    public static final String secretKey = "4P9slQYFTQeomzEMRj3Vq8n_WSv3IvbUvTSmwMwa";
+
+    /**
+     * 球队
+     */
+    public static final List<String> SPORTS = Arrays.asList("Basketball", "Soccer");
+    /**
+     * 即将售罄
+     */
+    public static final List<String> FINISHED = Arrays.asList("", "Basketball", "Soccer", "综合收藏", "other");
+}

+ 1003 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/constant/Constants.java

@@ -0,0 +1,1003 @@
+package com.mangoo.rating.recommend.constant;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 通用常量信息
+ * 
+ * @author zheng
+ */
+public class Constants {
+	/**
+	 * UTF-8 字符集
+	 */
+	public static final String UTF8 = "UTF-8";
+
+	/**
+	 * GBK 字符集
+	 */
+	public static final String GBK = "GBK";
+
+	/**
+	 * 通用成功标识
+	 */
+	public static final String SUCCESS = "0";
+
+	/**
+	 * 通用失败标识
+	 */
+	public static final String FAIL = "1";
+
+	/**
+	 * 登录成功
+	 */
+	public static final String LOGIN_SUCCESS = "Success";
+
+	/**
+	 * 注销
+	 */
+	public static final String LOGOUT = "Logout";
+
+	/**
+	 * 注册
+	 */
+	public static final String REGISTER = "Register";
+
+	/**
+	 * 登录失败
+	 */
+	public static final String LOGIN_FAIL = "Error";
+
+	/**
+	 * 当前记录起始索引
+	 */
+	public static final String PAGE_NUM = "pageNum";
+
+	/**
+	 * 每页显示记录数
+	 */
+	public static final String PAGE_SIZE = "pageSize";
+
+	/**
+	 * 排序列
+	 */
+	public static final String ORDER_BY_COLUMN = "orderByColumn";
+
+	/**
+	 * 排序的方向 "desc" 或者 "asc".
+	 */
+	public static final String IS_ASC = "isAsc";
+
+	/**
+	 * 参数管理 cache name
+	 */
+	public static final String SYS_CONFIG_CACHE = "sys-config";
+
+	/**
+	 * 参数管理 cache key
+	 */
+	public static final String SYS_CONFIG_KEY = "sys_config:";
+
+	/**
+	 * 字典管理 cache name
+	 */
+	public static final String SYS_DICT_CACHE = "sys-dict";
+
+	/**
+	 * 字典管理 cache key
+	 */
+	public static final String SYS_DICT_KEY = "sys_dict:";
+
+	/**
+	 * 资源映射路径 前缀
+	 */
+	public static final String RESOURCE_PREFIX = "/profile";
+	public static final String STOCK_GROUP_INFO = "STOCK_GROUP_INFO_";
+	public static final String AUTHENTICATION_TOKEN = "REDIS_AUTHENTICATION_TOKEN_";
+	public static final String AUTHORIZATION = "token";
+	public static final String ORDER_BY = "order by ";
+	public static final String STATUS_OK = "1";
+	public static final String ONE = "1";
+	public static final String ZERO = "0";
+	public static final String TWO = "2";
+	public static final String FINANCE_TYPE_ADD = "支付";
+	public static final String FINANCE_TYPE_RETURN = "退款";
+	public static final String PAY_CALLBACK_LOCK_KEY = "pay_callback_lock_key";
+	public static final String ORDER_CALLBACK_LOCK_KEY = "order_callback_lock_key";
+	public static final String TRADE_SUCCESS = "支付成功";
+	public static final String TRADE_SUCCESS_EN = "SUCCESS";
+	public static final String ALIPAY_TRADE_SUCCESS = "TRADE_SUCCESS";
+	public static final String ALIPAY_REFUND_SUCCESS = "REFUND_SUCCESS";
+	public static final String REDIRECT_PATH = "/pageB/productDetail/productDetail?id=";
+	public static final String TYPE_BANNER_DETAIL = "banner_detail";
+	public static final String QINIU_HOST = "https://static.public.hobbystock.cn";
+	public static final String MERCHANT_INFO_TYPE_SALE = "sale";
+	public static final String MERCHANT_INFO_TYPE_HISTORY = "history";
+	public static final String KEY_WORD = "keyword";
+	public static final String BANK_STR = " ";
+	public static final String EMPTY_STR = "";
+	public static final String AND_STR = "&";
+	public static final String USER_FACE_VERIFY = "USER_FACE_VERIFY_ID";
+	public static final String RY_TOKEN = "RY_TOKEN";
+	//商家拉黑类型
+	public static final @NotNull String DICT_TYPE_MER_PULL_BACK = "mer_pull_back";
+	public static long TIMES_DAY = 60 * 60 * 24 * 7;
+	public static long TIMES_30_DAY = 60 * 60 * 24 * 30;
+	public static long times = 1000 * TIMES_DAY;
+	public static final String OPTIONS = "OPTIONS";
+	public static final String ORDER_BUY_ALL_MARK = "ORDER_BUY_ALL_MARK";
+	public static final String CARD_INFO_MARK = "CARD_INFO_MARK_";
+	public static final String CARD_GOODS_ACT_SIGN = "CARD_GOODS_ACT_SIGN";
+	public static final String CARD_INFO_ACT_SIGN = "CARD_INFO_ACT_SIGN";
+	public static Map<String, Object> JSON_HEADS = new HashMap<String, Object>() {{
+		put("Content-Type", "application/json");
+	}};
+	public static final String JPUSH_TYPE_TAG = "tags";
+	public static final String JPUSH_TYPE_ID = "registrationIds";
+	public static final String ORDER_BUY_ALL_RETURN_TITLE = "亲爱的用户您好,感谢您对HOBBY STOCKS的支持,请理性参与购买,祝您玩卡愉快,得到好卡!";
+	public static final String CODE_TYPE_INCR = "CODE_TYPE_INCR_";
+	public static final String USER_CODE_TYPE_INCR = "USER_CODE_TYPE_INCR";
+	public static final String PRICE_FOR_NEW = "price_for_new";
+	public static String PUSH_TYPE_JPUSH = "jpush";
+	public static String PUSH_TYPE_SYSTEM = "system";
+	public static String PUSH_ACCEPT_TYPE_ALL = "all";
+	public static String PUSH_ACCEPT_TYPE_SETS = "sets";
+	public static String PUSH_ACCEPT_TYPE_GROUP = "group";
+	public static String PUSH_ACCEPT_TYPE_USER = "user";
+	public static String APP_VERSION_PROD = "prod";
+	public static String APP_VERSION_DEV = "dev";
+	/**
+	 * 买队玩法
+	 */
+	public static String INFO_TYPE_BUY_TEAM = "买队玩法";
+	/**
+	 * 选队随机
+	 */
+	public static String INFO_TYPE_SELECT_TEAM = "选队随机";
+	/**
+	 * 支持 转 剩余随机的玩法
+	 * ||买队,选队随机
+	 */
+	public static List<String> CAN_CHANGE_RATE = Arrays.asList(INFO_TYPE_BUY_TEAM, INFO_TYPE_SELECT_TEAM);
+	/**
+	 * 自选卡种
+	 */
+	public static String INFO_TYPE_SELECT_SETS = "自选卡种";
+	/**
+	 * 随机球队
+	 */
+	public static String INFO_TYPE_TEAM = "随机球队";
+	/**
+	 * 随机球员
+	 */
+	public static String INFO_TYPE_PLAYER = "随机球员";
+	/**
+	 * 随机卡种(区分编号)
+	 */
+	public static String INFO_TYPE_SETS = "随机卡种(区分编号)";
+	/**
+	 * 随机卡种(不区分编号)
+	 */
+	public static String INFO_TYPE_SETS_NUM = "随机卡种(不区分编号)";
+	/**
+	 * 选队随机 子分类:区域随机
+	 */
+	public static String INFO_SUB_TYPE_AREA_RANDOM = "区域随机";
+	public static String CACHE_GOODS_SETS = "APP_CACHE_GOODS_SETS_";
+	public static String CACHE_GOODS_TEAM = "APP_CACHE_GOODS_TEAM_";
+	public static String GROUP_INFO_MIX_MAX_PRICE = "GROUP_INFO_MIX_MAX_PRICE";
+	public static String GROUP_INFO_CLICK_COUNT = "GROUP_INFO_CLICK_COUNT";
+	public static String MERCHANT_APP_USER_LINK = "MERCHANT_APP_USER_LINK";
+	public static String INFO_SORT_RATE_TYPE = "info_sort_rate_type";
+	public static String SORT_RATE_TYPE_CLICK = "sort_rate_type_click";
+	public static String SORT_RATE_TYPE_SOLD_AMOUNT = "sort_rate_type_sold_amount";
+	public static String SORT_AMOUNT_RATE = "sort_amount_rate";
+	public static String SORT_AMOUNT_RATE_NEW = "sort_amount_rate_new";
+	public static String CARD_INFO_MARK_USER = "CARD_INFO_MARK_USER";
+	public static String CODE_USER_LINK = "CODE_USER_LINK";
+	public static String REFUND_TYPE = "REFUND_TYPE";
+	public static String REFUND_TYPE_CODE = "code";
+	public static String ORDER_MARK_USER_POINT = "ORDER_MARK_USER_POINT";
+	public static final String ORDER_MARK_INFO_MSG = "亲爱的卡迷用户,为了更好的营造平台用户购物体验,您购买的本组订单未支付(含订单支付超时)次数即将达到单日上限,若未及时支付,您将于24小时内无法购买本组商品。感谢您的配合,祝您玩卡愉快,获得心仪卡片!";
+	public static final String ORDER_MARK_INFO_MSG_END = "亲爱的卡迷用户,为了更好的营造平台用户购物体验,您购买的本组订单未支付(含订单支付超时)次数已达到单日上限,您将于24小时内无法购买本组商品。感谢您的配合,祝您玩卡愉快,获得心仪卡片!";
+	public static final String ORDER_MARK_APP_MSG = "亲爱的卡迷用户,为了更好的营造平台用户购物体验,您当日订单未支付(含订单支付超时)次数即将达到单日上限,若未及时支付,您将于24小时内无法购买本平台商品。感谢您的配合,祝您玩卡愉快,获得心仪卡片!";
+	public static final String ORDER_MARK_APP_MSG_END = "亲爱的卡迷用户,为了更好的营造平台用户购物体验,您当日订单未支付(含订单支付超时)次数已达到单日上限,您将于24小时内无法购买本平台商品。感谢您的配合,祝您玩卡愉快,获得心仪卡片!";
+	public static final String PHONE_CHECK = "PHONE_CHECK";
+	public static final String LIMIT_RULE = "LIMIT_RULE";
+	public static final String comma = ",";
+	public static final String dot = ".";
+	public static final String pound_key = "#";
+	public static final String comma2 = ",,";
+	public static final String comma3 = ",,,";
+	public static final String bank = " ";
+	public static final String sub = "-";
+	public static final String seq_ZN = "编";
+	public static final String NO = "No.";
+	public static final String WORK_WX_LIVE_OPEN = "work_wx_live_open";
+	public static final String XML_BACK_SUCCESS = " xmlBack = \"<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>\";";
+	public static String GROUP_PLAYBACKPATH = "GROUP_PLAYBACKPATH_";
+	public static String ORDER_CANCEL_PREFIX = "ORDERCANCELPREFIX_";
+	public static String ORDER_CANCEL_LOCK = "ORDER_CANCEL_LOCK_";
+	public static final String All = "all";
+	public static final String TRUE_STR = "T";
+	public static final String LIVE_TYPE_APPLET = "applet";
+	public static final String LIVE_TYPE_WORK_WX = "work_wx";
+	public static final String LIVE_TYPE_RONG_CLOUD = "rongcloud";
+	//腾讯直播
+	public static final String LIVE_TYPE_TENCENT_CLOUD = "tencent_cloud";
+	//火山直播
+	public static final String LIVE_TYPE_VOLC = "volc";
+	public static String EDIT_TYPE_PAY_PSD = "pay_psd";
+	public static String EDIT_TYPE_LOGIN_PSD = "login_psd";
+	public static String MSG_PAGE = "MSG_PAGE";
+	public static String HOT_TYPE = "HOT_TYPE";
+	public static String USER_PSD_COUNT = "USER_PSD_COUNT";
+	public static String USER_PAY_REMIND = "USER_PAY_REMIND";
+	public static final String UPDATE_GROUP_QUERY = "UPDATE_GROUP_QUERY_";
+	/**
+	 * 优惠劵类型:组团
+	 */
+	public static final String COUPON_TYPE_CARD = "card";
+	/**
+	 * 优惠劵类型:商品
+	 */
+	public static final String COUPON_TYPE_GOODS = "goods";
+	/**
+	 * 活动商品类型:虚拟商品,和sku无关了
+	 */
+	public static final String SKU_TYPE_VIRTUAL_GOODS = "virtual_goods";
+	/**
+	 * 订单类型:组团
+	 */
+	public static final String ORDER_TYPE_GROUP = "group";
+	/**
+	 * 订单类型:商城
+	 */
+	public static final String ORDER_TYPE_SHOP = "shop";
+	/**
+	 * 商品类型:积分
+	 */
+	public static final String SKU_TYPE_POINT = "point";
+	/**
+	 * 商城商品
+	 */
+	public static final String SKU_TYPE_GOODS = COUPON_TYPE_GOODS;
+	/**
+	 * 商品类型:纯金额
+	 */
+	public static final String SKU_TYPE_AMOUNT = "amount";
+	/**
+	 * 商品类型:混合
+	 */
+	public static final String SKU_TYPE_MIX = "mix";
+	/**
+	 * 商品活动类型:优惠劵
+	 */
+	public static final String SKU_ACT_TYPE_COUPON = "coupon";
+	/**
+	 * 人脸识别不提醒白名单
+	 */
+	public static String WHITELIST_FAVE_VERIFY_USER = "WHITELIST_FAVE_VERIFY_USER";
+	/**
+	 * spu库存锁key
+	 */
+	public static String SPU_SOLD_STOCK_LOCK = "SPU_SOLD_STOCK_LOCK";
+	public static String MALL_ORDER_DETAIL_URL = "/pageC/shoppingUser/myOrderDetials?id=";
+
+	public static final List<Integer> refundOrderStatus = Arrays.asList(101, 102, 103, 104, 105, 106, 301, 302);
+	/**
+	 * 商户积分
+	 */
+	public static final String POINT_TYPE_MERCHANT = "merchant";
+	public static final String POINT_NAME_MERCHANT = "商家活动积分";
+	/**
+	 * 商家活动前缀
+	 */
+	public static final String POINT_TYPE_MERCHANT_ACT_PRE = "mer_act";
+	/**
+	 * 平台活动前缀
+	 */
+	public static final String POINT_TYPE_HB_ACT_PRE = "hb_act";
+	public static final String POINT_NAME_HB_ACT = "HS活动积分";
+	/**
+	 * 平台活动前缀,需要订单完结
+	 */
+	public static final String POINT_TYPE_HB_ACT_OVER_PRE = "hb_act_over";
+	/**
+	 * 平台积分
+	 */
+	public static final String POINT_TYPE_COMMON = "common";
+	public static final String POINT_NAME_COMMON = "平台积分";
+	/**
+	 * 商城购物积分
+	 */
+	public static final String POINT_TYPE_GOODS = "goods";
+	public static final String POINT_NAME_GOODS = "商城积分";
+	/**
+	 * 积分消耗
+	 */
+	public static final String POINT_TYPE_COMMON_SUB = "common_sub";
+	/**
+	 * 积分活动
+	 */
+	public static final String POINT_TYPE_COMMON_ACT = "common_act";
+	public static final String POINT_TYPE_COMMON_ACT_SUB = "common_act_sub";
+	/**
+	 * 积分返回
+	 */
+	public static final String POINT_TYPE_COMMON_RETURN = "common_return";
+	/**
+	 * 活动积分-SP
+	 */
+	public static final String POINT_TYPE_ACT_SP = "SP";
+	public static final String POINT_NAME_ACT_SP = "SP积分";
+	/**
+	 * 商城,仅添加记录
+	 */
+	public static final String POINT_TYPE_ADD_RECORD = "record";
+	public static final String POINT_TYPE_ADD_RECORD_NEW = "common_record";
+	/**
+	 * 竞价积分-通用积分
+	 */
+	public static final String AUCTION_ORDER_COMMON_POINT = "auction_common_point";
+	public static final String AUCTION_ORDER_COMMON_AMOUNT = "auction_common_amount";
+	/**
+	 * 鸿运系列积分-lucky_point
+	 */
+	public static final String POINT_TYPE_ACT_LUCKY = "lucky_point";
+	/**
+	 * 用户成长值
+	 */
+	public static final String POINT_TYPE_USER_GROWTH = "user_growth";
+	public static final String POINT_NAME_USER_GROWTH = "会员成长值";
+	/**
+	 * 精美卡片积分类型前缀,后面拼上card_id
+	 */
+	public static final String POINT_TYPE_GIFT_CARD_PRE = "gift_card_";
+	public static final String POINT_NAME_GIFT_CARD = "精美卡片";
+	public static final String INFO_TEAM_PRICE = "info_team_price";
+	public static final String OTHER_STR = "OTHER";
+	public static final String TEAM_SIMPLE_NAME = "team_simple_name";
+	public static final String TEAM_SIMPLE_NAME_NEW = "team_simple_name_new";
+	public static final String INFO_SELECT_TEAM_CACHE = "INFO_SELECT_TEAM_CACHE";
+	public static final String INFO_ONLY_TEAM_CACHE = "INFO_ONLY_TEAM_CACHE_";
+	public static final String POINT_CHANGE_RMB = "point_rmb_rate";
+	//verify_user_act  实名用户活动
+	public static final String POINT_TYPE_VERIFY_USER_ACT = "verify_user_act";
+	//complete_order_act 消费达标活动
+	public static final String POINT_TYPE_COMPLETE_ORDER_ACT = "complete_order_act";
+	//act_task_labubu labubu抽奖活动
+	public static final String POINT_TYPE_ACT_LABUBU = "act_task_";
+
+	/**
+	 * 用户支付类型配置
+	 */
+	public static final String USER_PAY_TYPE_CONFIG = "user_pay_type_config";
+	/**
+	 * 订单随机支付类型配置
+	 */
+	public static final String PAY_TYPE_RANDOM_CONFIG = "pay_type_random_config";
+	/**
+	 * 每日支付金额统计
+	 */
+	public static final String PAY_AMOUNT_DAY_KEY = "pay_amount_day_";
+	/**
+	 * 每日支付次数统计
+	 */
+	public static final String PAY_COUNT_DAY_KEY = "pay_count_day_";
+	public static final String PAY_FAIL_COUNT = "pay_fail_count";
+	public static final String ENFORCE_FAVE_VERIFY_USER = "enforce_fave_verify_user_fail";
+	public static final String ENFORCE_FACE_VERIFY_REMIND = "enforce_face_verify_remind";
+	public static final BigDecimal RMB_ONE_CENT = new BigDecimal("0.01");
+	/**
+	 * 选队转随机
+	 */
+	public static final String INFO_TYPE_TEAM_TO_RANDOM = "team_to_random";
+	public static final String INFO_REAL_SOLD_COUNT = "info_real_sold_count_";
+	public static final String ALIPAY_MAIN_BF = "alipay-main-bf";
+	public static final String WXPAY_MAIN_BF = "wxpay-main-bf";
+
+	public static final String INFO_TEAM_TO_RANDOM_CACHE = "info_team_to_random_cache_";
+	public static final String CHINA = "china";
+	public static final String IP_CACHE = "app_ip_address_cache";
+	public static final String TEAM_CHANGE_RATE = "team_change_rate";
+	public static final String BUY_TEAM_CHANGE_RATE = "buy_team_change_rate";
+	public static final String DEFAULT_PAY_TYPE = "alipay";
+	public static final String ADD = "add";
+	public static final String SUB = "sub";
+	public static final String EVALUATION_LEVEL_MIDDLE = "middle";
+	public static final String EVALUATION_LEVEL_BAD = "bad";
+	public static final String EVALUATION_LEVEL_GOOD = "good";
+	public static final String GUESS_GOODS_CACHE = "guess_goods_cache_";
+	public static final String URL_LIMIT_COUNT = "url_limit_count";
+	//关注
+	public static final String FANS_TYPE_USER_LIKE = "user_like";
+	//用户拉黑
+	public static final String FANS_TYPE_USER_DISLIKE = "user_dislike";
+	//商家拉黑
+	public static final String FANS_TYPE_MERCHANT_DISLIKE = "merchant_dislike";
+	/**
+	 * 拉黑缓存key
+	 */
+	public static final String MERCHANT_DISLIKE_USER = "merchant_dislike_user";
+	public static final String USER_DISLIKE_MERCHANT = "user_dislike_merchant";
+	/**
+	 * 卡种字典表记录id
+	 */
+	public static final String INFO_SETS_RECORD_ID = "info_sets_record_id";
+	/**
+	 * 用户下单分布式key,同一时间仅能提交一种订单
+	 */
+	public static final String GROUP_ORDER_LOCK = "group_order_lock_";
+	/**
+	 * AES秘钥
+	 */
+	public static final String AES_TOKEN_PASSWORD = "C19CF02FC6A2040FC9A16BA5161F2D17";
+	public static final String AES_USER_PASSWORD = "E1C7AE96EFE6CD2CD84861C029C2E80D";
+	/**
+	 * 抢购token
+	 */
+	public static final String AES_TOKEN_CACHE = "AES_TOKEN_CACHE";
+	public static final String ACT_USER_TOKEN_CACHE = "ACT_USER_TOKEN_CACHE";
+	/**
+	 * 卡单用户限制
+	 */
+	public static final String ORDER_LIMIT_USER = "order_limit_user";
+	/**
+	 * 免验证手机号
+	 */
+	public static final List<String> NO_CHECK_PHONE = Arrays.asList("17317287633", "13071005103", "15618709098", "15679535223");
+	/**
+	 * 订单子类型,赠送订单接收
+	 */
+	public static final String ORDER_SUB_TYPE_RECEIVE = "receive";
+	/**
+	 * 订单子类型,奖品订单
+	 */
+	public static final String ORDER_SUB_TYPE_PRIZE = "prize_ship";
+	/**
+	 * 订单子类型,赠送订单:赠送
+	 */
+	public static final String ORDER_SUB_TYPE_GIVE_AWAY = "give_away";
+	public static final String SKU_COUPON_ID_CACHE = "sku_coupon_id_cache";
+	/**
+	 * 秒级别-半小时
+	 */
+	public static final int HALF_AN_HOUR = 1800;
+	/**
+	 * 秒级别-1小时
+	 */
+	public static final int ONE_HOUR = 3600;
+	/**
+	 * 秒级别-10分钟
+	 */
+	public static final int TEN_MINUTES = 600;
+
+	/**
+	 * 优惠劵使用域
+	 */
+	public static final String COUPON_SCOPE_CONFIG = "coupon_scope_config";
+	public static final String COUPON_SCOPE_CONFIG_CACHE = "coupon_scope_config_cache";
+	/**
+	 * 支付类型
+	 */
+	public static final String PAYMENT_TYPE = "payment_type";
+
+	public static final String DAY = "day";
+	public static final String HOUR = "hour";
+
+	public static final String ACT_PRIZE_CACHE = "act_prize";
+	public static final String ACT_PRIZE_INFO_CACHE = "act_prize_info";
+	public static final String ACT_PRIZE_POOL_CACHE = "act_prize_pool";
+	public static final String ACT_TASK_SHARE_CACHE = "act_task_share_cache:";
+	public static final String DEFAULT_STR = "default";
+	/**
+	 * 发票类型,个人
+	 */
+	public static final String INVOICE_TYPE_PERSON = "person";
+	/**
+	 * 发票类型,企业
+	 */
+	public static final String INVOICE_TYPE_COMPANY = "company";
+	public static final BigDecimal HUNDRED = new BigDecimal("100");
+
+	public static final BigDecimal ONE_THOUSAND = new BigDecimal("1000");
+	/**
+	 * sku库存key
+	 */
+	public static final String SKU_STOCK_PRE = "sku_stock_";
+	public static final String NUM_REG = "^[0-9]+$";
+	/**
+	 * 活动参与类型:积分
+	 */
+	public static final String ACT_SUB_TYPE_POINT = "point";
+	/**
+	 * 活动参与类型:次数
+	 */
+	public static final String ACT_SUB_TYPE_TIMES = "times";
+
+	public static final String COUPON_ACT_MERCHANT = "coupon_act_merchant";
+	/**
+	 * 卡密活动类型:商品
+	 */
+	public static final String GOODS_ACT_TYPE_GOODS = COUPON_TYPE_GOODS;
+	/**
+	 * 卡密活动类型:优惠劵
+	 */
+	public static final String GOODS_ACT_TYPE_COUPON = "coupon";
+	/**卡密活动类型:大转盘*/
+	/**
+	 * 卡密活动类型:大转盘
+	 */
+	public static final String GOODS_ACT_TYPE_TURNTABLE = "turntable";
+
+	public static final String CACHE_CALENDAR_LIST_SEARCH = "cache_calendar_list_search";
+	public static final String EVALUATION_TYPE_LIST_CALENDAR = "checklist_calendar";
+	/**
+	 * 完结组团状态
+	 */
+	public static final List<Integer> GROUP_FULL_STATUS = Arrays.asList(203, 204, 205, 2051, 206, 301);
+
+	/**
+	 * 随买随机分配卡密
+	 */
+	public static final String GROUP_RANDOM_ALL = "random_all";
+	/**
+	 * 选队随机
+	 */
+	public static final String GROUP_RANDOM_SELECT_TEAM = "random_select_team";
+	/**
+	 * 选队,区域随机
+	 */
+	public static final String GROUP_RANDOM_SELECT_CONFERENCE = "random_select_conference";
+	/**
+	 * 选队/区域转随机
+	 */
+	public static final String GROUP_RANDOM_SELECT_TO_RANDOM = "select_to_random";
+	/**
+	 * 买队,无随机
+	 */
+	public static final String GROUP_RANDOM_BUY_TEAM = "random_buy_team";
+	/**
+	 * 组满随机,暂不存在
+	 */
+	public static final String GROUP_RANDOM_FULL_ALL = "random_full_all";
+	/**
+	 * 买队转随机,暂不存在
+	 */
+	public static final String GROUP_RANDOM_BUY_TEAM_ALL = "random_buy_team_all";
+
+	public static final String RESTRICT_USER = "restrict_user"; // 黑名单用户字典type
+	public static final String RESTRICT_USER_CACHE = "restrict_user_cache"; // 黑名单用户信息key
+	public static final String RESTRICT_USER_ADDRESS = "restrict_user_address"; // 黑名单用户地址key
+	/**
+	 * 卡密来源-组队卡密
+	 */
+	public static final String GOODS_REF_TYPE_GROUP = "group";
+	public static final String CODE_TYPE_PRIZE = "prize";
+	/**
+	 * 公布抽奖结果回调url
+	 */
+	public static final String CODE_PUBLIC_CALL_BACK_URL = "http://poyee-app/api/local/public";
+	//不属于金额支付的支付类型
+	public static final List<String> payment_type = Arrays.asList("point", "积分抽奖", "活动赠送");
+
+	public static final String ES_CACHE_CARD_GROUP_INFO = "es_cache_card_group_info";
+
+	public static final String INDEX_NAME = "app_es_group";
+	//顺丰快递
+	public static final String SF_TOKEN = "sf_token";
+	public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
+	public static final String CONTENT_TYPE_JSON = "application/json";
+	public static final String express_level_type = "express_level";
+
+	public static final String stop_courier_mer = "stop_courier_mer";
+	/**
+	 * 一天的秒
+	 */
+	public static final Long dayForSecond = 86400L;
+	/**
+	 * 一周的毫秒
+	 */
+	public static final Long WEEK_OF_TIMESTAMP = 86400L * 7000;
+	/**
+	 * 仓管费
+	 */
+	public static final Integer warehouseFee = 0;
+	/**
+	 * 预售开卡订单key
+	 */
+	public static final String ALLOCATE_USER_GOODS_ORDER = "allocate_user_goods_order_";
+	/**
+	 * 会员初始化
+	 */
+	public static final String MEMBER_INIT_KEY = "app_user_member_init";
+
+	public static final String PAY_BOFOO = "bof";
+	public static final String PAY_ADAPAY = "adapay";
+
+	/**
+	 * 组队组齐后发放
+	 */
+	public static final String MERCHANT_ACT_GROUP_FULL = "merchant_act_group_full";
+	/**
+	 * 公示卡密后发放
+	 */
+	public static final String MERCHANT_ACT_PUBLIC_GOODS = "merchant_act_public_goods";
+	/**
+	 * 订单购买后分配
+	 */
+	public static final String MERCHANT_ACT_AFT_ORDER = "merchant_act_aft_order";
+	/**
+	 * 随机:订单购买后分配
+	 */
+	public static final String MERCHANT_ACT_RANDOM_AFT_ORDER = "merchant_act_random_aft_order";
+	/**
+	 * 随机:订单购买后分配
+	 */
+	public static final String MERCHANT_ACT_RANDOM_GROUP_FULL = "merchant_act_random_group_full";
+
+	public static final String payment_type_agreement = "agreement";
+
+	public static final String payment_type_bank = "bank";
+	public static final String payment_type_uac = "uac";
+	public static final String payment_type_alipay = "alipay";
+	/**
+	 * 正常订单
+	 */
+	public static final String order_type_normal = "normal";
+	/**
+	 * 需要退款订单
+	 */
+	public static final String order_type_refund = "refund";
+
+	public static final String lucky_point_2023_618 = "lucky_point_2023_618";
+	/**
+	 * 鸿运系列积分 618 -lucky_point
+	 */
+	public static final String POINT_TYPE_ACT_LUCKY_618 = "lucky_point_2023_618";
+	/**
+	 * hotType时间缓存
+	 */
+	public static final String APP_HOT_TYPE_TIMES_CACHE = "app_hot_type_times_cache";
+	/**
+	 * 昵称黑名单
+	 */
+	public static final String APP_NAME_BACK_LIST_CACHE = "app_name_back_list_cache";
+	/**
+	 * 用户标签:高频
+	 */
+	public static final String APP_USER_CAT_HIGH_FREQUENCY = "high_frequency";
+	public static final String APP_USER_CAT_TYPE_USER_PAY = "user_pay";
+
+	public static final String PHONE_TYPE_IOS = "IOS";
+
+	public static final String LIST_ACT_TYPE_SET = "list_act_all_set";
+	public static final String LIST_ACT_ALL_TEAM = "list_act_all_team";
+	public static final String LIST_ACT_ALL_PLAYER = "list_act_all_player";
+	public static final String LIST_ACT_SELECT_PLAYER = "list_act_select_player";
+	public static final String LIST_ACT_SELECT_TEAM = "list_act_select_team";
+	public static final String LIST_ACT_SELECT_SET = "list_act_select_set";
+	public static final String LIST_ACT_SELECT_NUM = "list_act_select_num";
+	/**
+	 * 球员球队list的key
+	 */
+	public static final String LIST_ACT_OF_PLAYER_TEAM = "list_act_of_set_player_team";
+	/**
+	 * 卡密收集活动类型
+	 */
+	public static final List<String> listActTypes = Arrays.asList("list_act_all_set", "list_act_all_team", "list_act_all_player",
+			"list_act_select_player", "list_act_select_team", "list_act_select_set", "list_act_select_num");
+
+	public static final String MEMBER_BENEFITS = "member_benefits";
+
+	public static final String BRAND_MEMBER_BENEFITS = "brand_member_benefits";
+	/**
+	 * 支付限流
+	 */
+	public static final String PAYMENT_TYPE_LIMIT_KEY = "payment_type_limit_key_";
+
+	public static final String USER_PAY_REMIND_DAY = "app_user_pay_remind_day";
+
+	public static final String USER_PAY_REMIND_MONTH = "app_user_pay_remind_month";
+	/**
+	 * 买退
+	 */
+	public static final String ALLOW_REFUND_SALE = "买退";
+	// 偏远地区
+	public static final String REMOTE_SHIPPING_FEE = "remote_shipping_fee";
+	/**
+	 * 消息类型
+	 */
+	public static final String APP_NOTIFY_TYPE_CONFIG = "app_notify_type_config";
+	/**
+	 * 消息显示大类型
+	 */
+	public static final String APP_NOTIFY_SHOW_CONFIG = "app_notify_show_config";
+	/**
+	 * 消息初始化类型
+	 */
+	public static final String APP_NOTIFY_INIT_CONFIG = "app_notify_init_config";
+
+	public static final String APP_CACHE_USER_NOTIFY_CONFIG = "app_cache_user_notify_config_";
+	/**
+	 * 物流订单号对应支付单号
+	 */
+	public static final String SHIPPING_TRADE_NO_TO_ORDER = "shipping_trade_no_to_order";
+
+	public static final String LIVE_EFFECTS = "live_effects";
+	/**
+	 * 支付编号对应订单
+	 */
+	public static final String PAY_TRADE_NO_TO_ORDER = "pay_trade_no_to_order";
+	/**
+	 * 组队卡密活动:选中卡密
+	 */
+	public static final String GROUP_ACT_TYPE_SELECT = "select";
+	/**
+	 * 组队卡密活动:随机卡密
+	 */
+	public static final String GROUP_ACT_TYPE_RANDOM = "random";
+	/**
+	 * 消费达标活动
+	 */
+	public static final String ACT_TYPE_CONSUME_LEVEL = "consume_level";
+	/**
+	 * 抽卡机活动
+	 */
+	public static final String ACT_TYPE_DRAW_CARD_GAME = "draw_card_game";
+	public static final String ACT_NAME_DRAW_CARD_GAME = "抽卡机活动币";
+	/**
+	 * 抽卡活动类型
+	 */
+	public static final String USER_DRAW_CARD_ACT = "user_draw_card_act";
+
+	public static final String ACT_BASE_SHIPPING_COST_KEY = "act_base_shipping_cost";
+
+	public static final BigDecimal ACT_BASE_SHIPPING_COST = new BigDecimal("9.9");
+
+
+	public static final String ACT_GOODS_TYPE_SET = "set";
+	public static final String ACT_GOODS_TYPE_TEAM = "team";
+	public static final String ACT_GOODS_TYPE_PLAYER = "player";
+	public static final String PART_COUPON = "part_coupon";
+	public static final String COLLECT_PANINI_ACT = "collect_panini_act";
+	//精美卡片key
+	public static final String GROUP_GIFT_CARD = "group_gift_card";
+	//精美卡片订单
+	public static final String ORDER_TYPE_GIFT_CARD = "gift_card";
+
+	public static final String ORDER_TYPE_DRAW = "draw";
+	;
+	//评级保证金
+	public static final String GRADE_EARNEST_AMOUNT = "grade_earnest_amount";
+
+	public static final String GRADE_INSURE_AMOUNT = "grade_insure_amount";
+
+	/**
+	 * 物流订单号对应支付单号
+	 */
+	public static final String GRADE_TRADE_NO_TO_ORDER = "grade_trade_no_to_order";
+	/**
+	 * 支付编号对应支付记录
+	 */
+	public static final String TRADE_NO_TO_PAY_RECORD = "trade_no_to_pay_record";
+	public static final String ANONYMOUS_NAME = "匿名用户";
+	public static final String ANONYMOUS_AVATAR = "https://static.public.hobbystock.cn/MC143302022060920611/avatar/1666182630730.jpg";
+
+	public static final String MERCHANT_POINT_SHOW = "merchant_point_show";
+	public static final String USER_SUB_POINT_LOCK = "user_sub_point_lock";
+	/**
+	 * 拼团赠送红包大转盘key
+	 */
+	public static final String INFO_GIVE_DRAW = "info_give_draw";
+	/**
+	 * 拼团618砍一刀
+	 */
+	public static final String ACT_GROUP_DECR_AMOUNT = "group_decr_amount";
+	public static String GROUP_USER_SHARE_24_618 = "group_user_share_24_618";
+	public static String ACT_CARD_REMOVAL = "act_card_removal";
+	public static String SEND_LIKE_ACT_PRIZE_ORDERS = "send_like_act_prize_orders";
+	public static String ACT_LIMIT_NUM_618 = "act_limit_num_618";
+	public static String email_notice_hzt = "hzt1212520@126.com";
+	/**
+	 * 私域显示
+	 */
+	public static String PRIVATE_SHOW_TYPE = "private_show_type";
+    /**
+     * 商家排序
+     */
+    public static String MERCHANT_POPULAR_RANK = "merchant_popular_rank";
+    /**
+     * 首页分类
+     */
+    public static String HOME_SPORT_LIST = "home_sport_list";
+	/**
+	 * 商家赠礼活动
+	 */
+	public static final String MERCHANT_ACT_GIFT = "merchant_act_send_gift";
+
+	public static final String TYPE_SHOP_POINT = "shop_order_point";
+	/**
+	 * 即将售罄查询每个商家个数-key
+	 */
+	public static final String ALMOST_FINISHED_NUM = "almost_finished_num";
+    /**
+     * 商家搜索字典
+     */
+    public static final String MERCHANT_SEARCH = "merchant_search";
+	/**
+	 * 好卡点赞
+	 */
+	public static final String FOLLOW_TOP_CARD_LIKE = "top_card_like";
+	/**
+	 * 首页红包列表
+	 */
+	public static final String FIRST_PAGE_DRAW_ACT = "first_page_draw_act";
+	//用户高频支付限制数量key
+	public static final String USER_PAY_LIMIT = "user_pay_limit";
+	/**
+	 * 用户消费限额提示key
+	 */
+	public static final String USER_LIMIT_PAY_NOTICE_KEY = "user_limit_pay_notice_%s_%s";
+	/**
+	 * 用户消费限额配置
+	 */
+	public static final String USER_LIMIT_PAY_CONFIG = "user_limit_pay_config";
+	/**
+	 * 用户提额问卷
+	 */
+	public static final String TYPE_USER_INCR_AMOUNT = "user_incr_amount";
+	/**
+	 * 用户月度提醒
+	 */
+	public static final String REFUSE_CARD_NOTICE = "refuse_card_notice:";
+	/**
+	 * 商家集团活动积分前缀
+	 */
+	public static final String POINT_TYPE_MERCHANT_GROUP_ACT_PRE = "mer_group_act";
+	/**
+	 * 商品购买资格
+	 */
+	public static final String PRIZE_REDUCTION_BUY_NUM = "reduction_buy_num";
+	public static final String USER_BUY_GROUP_INFO_KEY = "user_buy_group_info_%s_%s";
+	public static final String USER_CONTAINS_CAT_KEY_PRE = "user_cat_key_%s_%s_%s";
+	public static final String APP_CAT_SEARCH_ORDER = "user_search_order";
+	/**
+	 * 拼团活动积分倍率
+	 */
+	public static final String GROUP_ACT_RATE_PRE = "group_act_rate_";
+	/**
+	 * 拼团消费达标积分活动
+	 */
+	public static final String CONSUME_GROUP_ACT_POINT = "consume_group_act_point";
+	/**
+	 * 拼团组齐次数达标
+	 */
+	public static final String CONSUME_GROUP_FULL_COUNT = "consume_group_full_count";
+	/**
+	 * 新版退款模式,异步mq消费
+	 */
+	public static final String ASYNC_REFUND_TYPE = "amount_async";
+	/**
+	 * 退款模式2,仅退款,不更新订单状态
+	 */
+	public static final String REFUND_TYPE_AMOUNT_ONLY = "amount_only";
+	public static final String REFUND_SUB_TYPE_AMOUNT = "amount";
+	public static final String REFUND_SUB_TYPE_POINT = "point";
+	public static final String ACT_GOODS_COMMON_SORT = "act_goods_common_sort";
+
+
+	public static final String LUCK_BAG_ONLINE_REDIS_KEY = "luck_bag_online_redis_key_";
+	public static final String LUCK_BAG_JOIN_REDIS_KEY = "luck_bag_join_redis_key_";
+	public static final String GROUP_FULL_COUNT = "group_full_count";
+	public static final String GROUP_ACT_RATE = "group_act_rate";
+
+	/**
+	 * 生态购商家id
+	 */
+	public static final List<Long> GROUP_SALE_MERCHANT_IDS = Arrays.asList(31L, 1430L);
+	//拼团详情关注类型
+	public static final String GROUP_DETAIL_FOLLOW = "group_detail_follow";
+	//app商家指定权限
+	public static final String APP_MER_PERMISSIONS = "app_mer_permissions";
+	//直播间用户key
+	public static final String LIVING_ROOM_USER_KEY="living_room_user_key:";
+	//商家名下用户订单数量
+	public static final String MERCHANT_USER_ORDER_NUM="merchant_user_order_num:";
+	//用户变动
+	public static final String LIVING_USER_CHANGE_RECORD="living_user_change_record:";
+	/**拼团退款申请*/
+	public static final String REFUND_TYPE_GROUP = "group_refund";
+	/**拼团下架退款申请*/
+	public static final String REFUND_SUB_TYPE_OFF = "refund_off";
+	//订单退款状态
+	public static final List<Integer> REFUND_STATUS_LIST=Arrays.asList(204,205,206);
+	//直播间点赞key
+	public static final String LIVING_ROOM_LIKE_KEY="living_room_like_key:";
+	//直播间点赞人和点赞数key
+	public static final String LIVING_ROOM_LIKE_USER_KEY="living_room_like_user_key:";
+	//直播间点赞通知延时key
+	public static final String LIVING_ROOM_LIKE_DELAY_KEY="living_room_like_delay_key:";
+	//直播间点赞DB延时队列
+	public static final String LIVING_ROOM_LIKE_DB_DELAY_KEY="living_room_like_db_delay_key:";
+	/** 品牌会员积分前缀 **/
+	public static final String BRAND_POINT_TYPE_PRE = "diy_brand_point_";
+	/** 品牌会员成长值前缀 **/
+	public static final String BRAND_GROWTH_TYPE_PRE = "diy_brand_growth_";
+	/** 品牌会员初始化key **/
+	public static final String BRAND_MEMBER_INIT_KEY = "brand_member_init";
+	//精美卡片订单运费记录key
+	public static final String COST_ORDER_RECORD = "cost_order_record";
+	/** 商城首单券 **/
+	public static final String FIRST_ORDER_MERCHANT = "first_order_merchant";
+
+	public static final String APP_SEARCH_CACHE_KEY = "search_api_cache:app_search_cache_key_";
+
+	/** 签到 */
+	public static final String APP_ACT_TASK_TYPE_SIGNIN = "signIn";
+	/** 拼团支付 */
+	public static final String APP_ACT_TASK_TYPE_GROUPPAYMENT = "groupPayment";
+	/** 全平台支付 */
+	public static final String APP_ACT_TASK_TYPE_PAYMENT = "payment";
+	/** 商城商品支付 */
+	public static final String APP_ACT_TASK_TYPE_GOODSPAYMENT = "goodsPayment";
+	/** 拼团完成 */
+	public static final String APP_ACT_TASK_TYPE_GROUPCOMPLETEPAYMENT = "groupCompletePayment";
+	/** 支付完成 */
+	public static final String APP_ACT_TASK_TYPE_PAYCOMPLETEPAYMENT = "payCompletePayment";
+	/** 邀请 */
+	public static final String APP_ACT_TASK_TYPE_INVTATION = "invitation";
+
+
+	//商家邀请码配置的优惠券
+	public static final String DICT_TYPE_INVITE_CODE = "merchant_invite_code";
+	/**
+	 * 商家发货锁单
+	 */
+	public static final String REDIS_KEY_MERCHANT_DELIVERY_LOCK = "merchant_delivery_lock:";
+	// 时间格式
+	public static final String DATETIME_FORMAT = "YYYY.MM.DD HH:mm:ss";
+	public static final String DATE_FORMAT = "YYYY.MM.DD";
+
+	// 支付相关
+	public static final String PAY_TYPE_WECHAT = "WECHAT_PAY";
+	public static final int PAY_EXPIRE_MINUTES = 30;
+	public static final int PSA_PAY_EXPIRE_HOURS = 24;
+
+	// 图片相关
+	public static final String IMAGE_ALLOW_SUFFIX = "jpg,png,jpeg";
+	public static final long IMAGE_MAX_SIZE = 5 * 1024 * 1024;
+	public static final int CARD_PHOTO_MIN_COUNT = 2;
+
+	// 分页默认配置
+	public static final int DEFAULT_PAGE_NUM = 1;
+	public static final int MINIAPP_PAGE_SIZE = 20;
+	public static final int ADMIN_PAGE_SIZE = 10;
+
+	// 优惠券相关
+	public static final int COUPON_RECEIVE_LIMIT = 1;
+	public static final String COUPON_TYPE_FULL_REDUCE = "FULL_REDUCE";
+	public static final String COUPON_SCOPE_ALL = "ALL";
+	public static final String COUPON_SCOPE_PSA = "PSA";
+
+	// 物流相关
+	public static final String LOGISTICS_COMPANY_SF = "SF";
+
+	// 逻辑删除标识
+	public static final int DEL_FLAG_NORMAL = 0;
+	public static final int DEL_FLAG_DELETED = 1;
+
+	public static final String CAM_01 = "cam01";
+
+	public static final Long ORDER_EXPIRE_MINUTES = 30L;
+	public static final Long ORDER_EXPIRE_30_MINUTES = 30L  * 60L * 1000L;
+	public static final Long ORDER_EXPIRE_15_MINUTES = 15L  * 60L * 1000L;
+
+
+}

+ 11 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/constant/DictConstants.java

@@ -0,0 +1,11 @@
+package com.mangoo.rating.recommend.constant;
+
+/**
+ * 字典类常量
+ */
+public class DictConstants {
+    public static final String STOP_UPDATE_VERSION = "stop_update_version";
+    public static final String APP_ONLINE_VERSION = "app_online_version";
+    public static final String APP_ONLINE_VERSION_IOS = "app_online_version_ios";
+    public static final String APP_ONLINE_VERSION_ANDROID = "app_online_version_android";
+}

+ 72 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/constant/RedisKeyManager.java

@@ -0,0 +1,72 @@
+package com.mangoo.rating.recommend.constant;
+
+
+/**
+ * 缓存管理类
+ *
+ * @author: gengjintao
+ * @date: 2026/01/20
+ */
+
+
+public class RedisKeyManager {
+    // 缓存键前缀定义
+    public static final String ACTIVITY = "rating:activity:";
+    public static final String MERCHANT = "rating:merchant:";
+    public static final String ORDER = "rating:order:status_update";
+    public static final String DICT = "rating:dict:";
+
+    /**
+     * 订单创建用户ID缓存Key
+     */
+    public static final String ORDER_CREATE_USERID_KEY = "rating:order:create:%s";
+    public static final String ORDER_PAID_KEY = "rating:order:paid:%s";
+    /**
+     * 调取摄像头服务-结束录信息
+     */
+    public static final String RATING_ORDER_CAMERA_KEY = "rating:card:camera:%s:%s";
+
+
+    /**
+     * 用户角色缓存Key
+     */
+    public static final String USER_PERMISSION_KEY_PREFIX = "sys:permission:user:%s";
+    /**
+     * 角色权限缓存Key
+     */
+    public static final String ROLE_PERMISSION_KEY_PREFIX = "sys:permission:role:%s";
+    /**
+     * 获取用户角色类型缓存Key
+     */
+    public static final String USER_ROLE_TYPE_KEY_PREFIX = "sys:role_type:user:%s";
+    /**
+     * 获取用户角色编码缓存Key
+     */
+    public static final String USER_ROLE_CODE_KEY_PREFIX = "sys:role_code:user:%s";
+    /**
+     * 获取产品套餐缓存Key
+     */
+    public static final String RATING_ORDER_PRODUCT_KEY = "rating:order:product:%s";
+
+    /**
+     * 获取服务时效缓存Key
+     */
+    public static final String RATING_ORDER_SERVICE_LEVEL_KEY = "rating:order:service:level:%s";
+
+    /**
+     * MQ消息幂等Key,防止重复消费
+     * %s 为消息UUID
+     */
+    public static final String MQ_IDEMPOTENT_KEY = "rating:mq:idempotent:%s";
+
+    /**
+     * 订单创建MQ消息幂等Key 防止重复消费
+     */
+    public static final String ORDER_CREATE_MQ_LOCK_KEY = "rating:order:create:mq:idempotent-lock:%s";
+
+    /**
+     * 订单超时MQ消息幂等Key 防止重复消费
+     */
+    public static final String ORDER_TIMEOUT_MQ_LOCK_KEY = "rating:order:timeout:mq:idempotent-lock:%s";
+
+}

+ 33 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/constant/RetConstants.java

@@ -0,0 +1,33 @@
+package com.mangoo.rating.recommend.constant;
+
+/**
+ * 返回值常量
+ */
+public enum RetConstants {
+    SUCCESS(0, "成功"),
+    ERROR(1, "失败"),
+    ERROR_PARAM(2, "参数错误"),
+    ERROR_NOT_LOGIN(500, ""),
+    ERROR_NOT_AUTH(4, "无权限"),
+    ERROR_NOT_EXIST(5, "不存在"),
+    ERROR_NOT_FOUND(501, "获取七牛token失败!"),
+    ERROR_NOT_SUPPORT(7, "不支持"),
+    ERROR_NOT_ALLOW(8, "不允许"),
+    ERROR_NOT_SUPPORT_OPERATION(9, "不支持的操作");
+
+    private final int code;
+    private final String message;
+
+    RetConstants(int code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+}

+ 74 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/constant/ShiroConstants.java

@@ -0,0 +1,74 @@
+package com.mangoo.rating.recommend.constant;
+
+/**
+ * Shiro通用常量
+ * 
+ * @author zheng
+ */
+public class ShiroConstants
+{
+    /**
+     * 当前登录的用户
+     */
+    public static final String CURRENT_USER = "currentUser";
+
+    /**
+     * 用户名
+     */
+    public static final String CURRENT_USERNAME = "username";
+
+    /**
+     * 消息key
+     */
+    public static final String MESSAGE = "message";
+
+    /**
+     * 错误key
+     */
+    public static final String ERROR = "errorMsg";
+
+    /**
+     * 编码格式
+     */
+    public static final String ENCODING = "UTF-8";
+
+    /**
+     * 当前在线会话
+     */
+    public static final String ONLINE_SESSION = "online_session";
+
+    /**
+     * 验证码key
+     */
+    public static final String CURRENT_CAPTCHA = "captcha";
+
+    /**
+     * 验证码开关
+     */
+    public static final String CURRENT_ENABLED = "captchaEnabled";
+
+    /**
+     * 验证码类型
+     */
+    public static final String CURRENT_TYPE = "captchaType";
+
+    /**
+     * 验证码
+     */
+    public static final String CURRENT_VALIDATECODE = "validateCode";
+
+    /**
+     * 验证码错误
+     */
+    public static final String CAPTCHA_ERROR = "captchaError";
+
+    /**
+     * 登录记录缓存
+     */
+    public static final String LOGINRECORDCACHE = "loginRecordCache";
+
+    /**
+     * 系统活跃用户缓存
+     */
+    public static final String SYS_USERCACHE = "sys-userCache";
+}

+ 39 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/constant/UserType.java

@@ -0,0 +1,39 @@
+package com.mangoo.rating.recommend.constant;
+
+/**
+ * 用户类型
+ */
+public class UserType {
+    //微信公众号
+    public static String WX_WEB = "WX_WEB";
+    //微信小程序
+    public static String WX_APPLET = "WX_APPLET";
+    //第三方
+    public static String THIRD = "THIRD";
+    //第三方-安卓
+    public static String THIRD_APK = "THIRD_APK";
+    //第三方-ios
+    public static String THIRD_APP = "THIRD_APP";
+    //苹果授权登陆
+    public static String APPLE_AUTH = "APPLE_AUTH";
+    //微信授权登陆
+    public static String WX_AUTH = "WX_AUTH";
+    //角色
+    public static final String USER_ROLE_SHIPPING = "shipping";
+    public static final String USER_ROLE_GENERAL_USER = "general_user";
+    public static final String USER_ROLE_ADMIN = "admin";
+
+    // 拼团 publicity
+    public static final String USER_ROLE_PUBLICITY = "merchant_sub_publicity";
+    // 物流 delivery
+    public static final String USER_ROLE_DELIVERY = "merchant_sub_delivery";
+
+    // 主播
+    public static final String USER_ROLE_LIVING = "living";
+    //运营
+    public static final String USER_ROLE_OPERATE_USER = "operate_user";
+    //生态购操作权限
+    public static final String USER_ROLE_MERCHANT_OPERATE_CODE = "merchant_operate_code";
+    //生态购角色-入库
+    public static final String USER_ROLE_ECOLOGICAL_SHOPPING = "ecological_shopping";
+}

+ 283 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/core/BaseController.java

@@ -0,0 +1,283 @@
+package com.mangoo.rating.recommend.core;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.mangoo.rating.recommend.AjaxResult;
+import com.mangoo.rating.recommend.MsgResult;
+import com.mangoo.rating.recommend.page.TableDataInfo;
+import com.mangoo.rating.recommend.page.TableSupport;
+import com.mangoo.rating.recommend.exception.ServiceException;
+import com.mangoo.rating.recommend.utils.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.beans.PropertyEditorSupport;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * web层通用数据处理
+ *
+ * @author gengjintao
+ */
+@Component
+public class BaseController {
+    protected final Logger logger = LoggerFactory.getLogger(BaseController.class);
+    private Object param;
+    private Object service;
+    private String methodName;
+    protected MinioUtils minioUtils;
+    @Value("${minio.url:http://static-img-dev.hobbystocks.cn}")
+    protected String url ;
+    @Value("${minio.access:aWRY0ZrODUHQtmOh}")
+    protected  String access ;
+    @Value("${minio.secret:MLMhFFV4B7kQqtaJJGqmbkqFasdxUxyO}")
+    protected  String secret ;
+    @Value("${minio.bucket:finance-audit}")
+    protected  String bucket ;
+    @Resource
+    @Qualifier("threadPoolTaskExecutor")
+    private ThreadPoolTaskExecutor pools;
+
+    /**
+     * 将前台传递过来的日期格式的字符串,自动转化为Date类型
+     */
+    @InitBinder
+    public void initBinder(WebDataBinder binder) {
+        // Date 类型转换
+        binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
+            @Override
+            public void setAsText(String text) {
+                setValue(DateUtils.parseDate(text));
+            }
+        });
+    }
+    /**
+     * 获取分页参数
+     */
+    public Integer getPageNum(){
+        return TableSupport.buildPageRequest().getPageNum();
+    }
+
+    /**
+     * 获取分页参数
+     */
+    public Integer getPageSize(){
+        return TableSupport.buildPageRequest().getPageSize();
+    }
+
+    /**
+     * 构建MyBatis-Plus分页对象
+     */
+    protected <T> Page<T> buildPage() {
+        return PageUtils.buildPage();
+    }
+
+    /**
+     * 构建MyBatis-Plus分页对象(带排序)
+     */
+    protected <T> Page<T> buildPageWithOrder() {
+        return PageUtils.buildPageWithOrder();
+    }
+
+    /**
+     * 获取request
+     */
+    public HttpServletRequest getRequest() {
+        return ServletUtils.getRequest();
+    }
+
+    /**
+     * 获取response
+     */
+    public HttpServletResponse getResponse() {
+        return ServletUtils.getResponse();
+    }
+
+    /**
+     * 获取session
+     */
+    public HttpSession getSession() {
+        return getRequest().getSession();
+    }
+
+    /**
+     * 响应请求分页数据(MyBatis-Plus)
+     */
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    protected TableDataInfo getDataTable(IPage<?> page) {
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(0);
+        rspData.setRows(page.getRecords());
+        rspData.setTotal(page.getTotal());
+        return rspData;
+    }
+
+    /**
+     * 响应返回结果
+     *
+     * @param rows 影响行数
+     * @return 操作结果
+     */
+    protected AjaxResult toAjax(int rows) {
+        return rows > 0 ? success() : error();
+    }
+
+    protected MsgResult toReturn(int rows) {
+        return rows > 0 ? MsgResult.success() : MsgResult.error();
+    }
+
+    protected MsgResult toReturn(int rows, String msg) {
+        return rows > 0 ? MsgResult.success(msg) : MsgResult.error(msg);
+    }
+
+    protected MsgResult toReturn(int rows, String msg, String data) {
+        return rows > 0 ? MsgResult.success(msg, data) : MsgResult.error(msg, data);
+    }
+
+    /**
+     * 响应返回结果
+     *
+     * @param result 结果
+     * @return 操作结果
+     */
+    protected AjaxResult toAjax(boolean result) {
+        return result ? success() : error();
+    }
+
+    /**
+     * 返回成功
+     */
+    public AjaxResult success() {
+        return AjaxResult.success();
+    }
+
+    /**
+     * 返回失败消息
+     */
+    public AjaxResult error() {
+        return AjaxResult.error();
+    }
+
+    /**
+     * 返回成功消息
+     */
+    public AjaxResult success(String message) {
+        return AjaxResult.success(message);
+    }
+
+    /**
+     * 返回失败消息
+     */
+    public AjaxResult error(String message) {
+        return AjaxResult.error(message);
+    }
+
+    public MsgResult errorMsg(String message) {
+        return MsgResult.error(message);
+    }
+
+    /**
+     * 返回错误码消息
+     */
+    public AjaxResult error(AjaxResult.Type type, String message) {
+        return new AjaxResult(type, message);
+    }
+
+    /**
+     * 页面跳转
+     */
+    public String redirect(String url) {
+        return StringUtils.format("redirect:{}", url);
+    }
+
+    /**
+     * 自动校验参数,不符则抛异常
+     *
+     * @param bindingResult
+     */
+    public void autoCheckParam(BindingResult bindingResult) {
+        if (bindingResult.hasErrors()) {
+            logger.info("参数异常:{}", bindingResult.getFieldError().getDefaultMessage());
+            throw new ServiceException("参数异常:" + bindingResult.getFieldError().getDefaultMessage());
+        }
+    }
+
+    /**
+     * 设置配置
+     */
+    protected void initConfig(Object service, String methodName, Object param) {
+        this.service = service;
+        this.param = param;
+        this.methodName = methodName;
+    }
+
+    private Object getValueByField(String fieldName,Class<?> clazz) {
+        try {
+            Field field = this.param.getClass().getDeclaredField(fieldName);
+            field.setAccessible(true);
+            Object value = field.get(this.param);
+            if(clazz.getName().equals(value.getClass().getName())){
+                return value;
+            }
+            return field.get(this.param);
+        } catch (NoSuchFieldException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }catch (NullPointerException ne){
+            ne.printStackTrace();
+        }
+        return null;
+    }
+
+    private void extracted(Field field,String columnsearch, int offset) {
+        try {
+            if(StringUtils.isNotEmpty(columnsearch) && !"null".equals(columnsearch)){
+                field.set(this.param, columnsearch + " limit 1000 " +( offset > 0 ? " offset  "+ offset :"" ));
+            }else{
+                field.set(this.param, " limit 1000 " +( offset > 0 ? " offset  "+ offset :"" ));
+            }
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }catch (NullPointerException ne){
+            ne.printStackTrace();
+        }
+    }
+
+    public List searchList() {
+        try {
+            Method method = service.getClass().getMethod(methodName, param.getClass());
+            return (List) method.invoke(service, param);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public Object sendByBean(String beanName,String methodName,Object...param){
+        try {
+            Object classBean = SpringUtils.getBean(beanName);
+            Method method = classBean.getClass().getMethod(methodName, JSONObject.class);
+            return method.invoke(classBean, param);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}

+ 81 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/core/service/BaseService.java

@@ -0,0 +1,81 @@
+package com.mangoo.rating.recommend.core.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.mangoo.rating.recommend.AjaxResult;
+import com.mangoo.rating.recommend.page.TableDataInfo;
+import com.mangoo.rating.recommend.utils.PageUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.function.Function;
+
+@Slf4j
+@Service("commonBaseService")
+public class BaseService {
+    /**
+     * 响应请求分页数据(MyBatis-Plus)
+     */
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    protected TableDataInfo getDataTable(IPage<?> page) {
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(0);
+        rspData.setRows(page.getRecords());
+        rspData.setTotal(page.getTotal());
+        return rspData;
+    }
+
+    /**
+     * 构建MyBatis-Plus分页对象
+     */
+    protected <T> Page<T> buildPage() {
+        return PageUtils.buildPage();
+    }
+
+    /**
+     * 构建MyBatis-Plus分页对象(带排序)
+     */
+    protected <T> Page<T> buildPageWithOrder() {
+        return PageUtils.buildPageWithOrder();
+    }
+
+    /**
+     * 构建MyBatis-Plus分页对象(指定页码和每页大小)
+     */
+    protected <T> Page<T> buildPage(Integer pageNum, Integer pageSize) {
+        return PageUtils.buildPage(pageNum, pageSize);
+    }
+
+    public AjaxResult success(){
+        return AjaxResult.success();
+    }
+
+    public AjaxResult success(String msg){
+        return AjaxResult.success(msg);
+    }
+
+    public AjaxResult error(){
+        return AjaxResult.error();
+    }
+
+    public AjaxResult error(String msg){
+        return AjaxResult.error(msg);
+    }
+
+
+    protected  TableDataInfo getDataTable(){
+        return new TableDataInfo();
+    }
+
+    /**
+     * 获取其他服务返回的tableData
+     * @return
+     */
+    protected TableDataInfo getTableDataByOtherService(Function function){
+
+
+        return getDataTable();
+    }
+
+
+}

+ 179 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/AppUserInfoDto.java

@@ -0,0 +1,179 @@
+package com.mangoo.rating.recommend.dto;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.mangoo.rating.recommend.utils.Base64Util;
+import lombok.Data;
+import lombok.ToString;
+import org.slf4j.MDC;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+@Data
+@ToString
+public class AppUserInfoDto implements Serializable {
+	private static final long serializableUID = 1L;
+	//id
+	private Integer id;
+	//openid
+	private String openid;
+	//unionid
+	private String unionid;
+	//账号
+	private String account;
+	//真实姓名
+	private String realname;
+	//昵称
+	private String nickname;
+	private String username;
+	//头像
+	private String avatar;
+	//会员等级
+	private Integer level;
+	//积分
+	private Long point;
+	//手机号
+	private String phone;
+	//邮箱
+	private String email;
+	//角色id
+	private Integer roleId;
+	//角色编码
+	private String rolecode;
+	//注册时间
+	private Date registerTime;
+	//注册渠道(用户类型) WX_WEB = 公众号, WX_APPLET = 小程序 , THIRD = 第三方
+	private String registerChannel;
+	//会员成长值
+	private Long growthNum;
+	//会员码
+	private String code;
+	/**
+	 * 是否接受通知
+	 */
+	private Integer notifyFlag;
+	/**
+	 * 极光推送
+	 */
+	private String smsRegisterId;
+	private String notifyType;
+	private Integer faceVerify;
+	/**
+	 * 支付开关,1开启,0关闭
+	 */
+
+	private Integer openPsd;
+	/**
+	 * 支付密码
+	 */
+	@JsonIgnore
+	private String loginPsd;
+	/**
+	 * 登陆密码
+	 */
+	@JsonIgnore
+	private String payPsd;
+	private Boolean payPsdExist;
+	private Boolean loginPsdExist;
+	private Integer refusePickUp;
+	private Integer num;
+	/**
+	 * 限额额度,默认1万,prop1字段
+	 */
+	private String payAmountOfDay;
+	/**
+	 * 开启限额,prop2字段
+	 */
+	private String openPayAmount;
+	/**
+	 * 悬浮窗口开关
+	 */
+	private Integer windowOpen;
+	/**
+	 * 身份证
+	 */
+	private String certName;
+	/**
+	 * 阿里账户
+	 */
+	private String alipayAccount;
+	/**
+	 * 1删除
+	 */
+	private Integer delFlg;
+	/**
+	 * 1开票权限
+	 */
+	private Integer openInvoice;
+	/**
+	 * 用户状态,正常:0,锁定:1,限购:2
+	 */
+	private Integer accountStatus;
+	/**
+	 * 可用支付类型
+	 */
+	private Integer blacklist;
+	private String idCard;
+	/**
+	 * 会员等级
+	 */
+	private Integer memberLevel;
+	/**
+	 * 会员名称
+	 */
+	private String memberName;
+	/**
+	 * 当月已获取成长值
+	 */
+	private Integer currentMonthGrowth;
+	/**
+	 * 月初初始化标志,默认0:未开始,1:已初始化
+	 */
+	private Integer memberInitFlag;
+	/**
+	 * 当前会员保级所需成长值
+	 */
+	private Integer memberKeepGrowth;
+
+	private boolean payInfoFlag;
+	private BigDecimal memberDiscount; // 会员折扣
+	//用户身份信息
+	@JsonIgnore
+	private String userCertData;
+	// open_card_show
+	private Integer openCardShow;
+
+	private String effectsType;
+
+	private Integer dailyLimit;
+	private Integer weeklyLimit;
+	private Integer monthlyLimit;
+	// 国际区号
+	private String diallingCode;
+	//商家拉黑类型:
+	private String merBackType;
+
+	@JsonIgnore
+	private Map<String, Object> baseUserHeader;
+
+	public Map<String, Object> getBaseUserHeader() {
+		JSONObject data = new JSONObject();
+		data.put("id", this.id);
+		data.put("nickname", this.nickname);
+		data.put("avatar", this.avatar);
+		data.put("tenantId", 1);
+		try {
+			String userStr = Base64Util.Base64Encode(data.toJSONString()).replaceAll("\r|\n", "");
+			Map<String, Object> header = new HashMap<>();
+			header.put("X-USER-BASE64", userStr);
+			header.put("TRACE_ID", MDC.get("TRACE_ID"));
+			return header;
+		} catch (Exception e) {
+		}
+		return null;
+	}
+}

+ 46 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/DictPropDTO.java

@@ -0,0 +1,46 @@
+package com.mangoo.rating.recommend.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author by po'yi
+ * @Classname DictPropDTO
+ * @Date 2023/7/24 11:29
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DictPropDTO implements Serializable {
+
+	/**开启时间,0-24小时*/
+	private Integer startTime=0;
+	/**结束时间,0-24小时*/
+	private Integer endTime=24;
+	/**指定组队类型,多个用逗号隔开*/
+	private String infoType;
+
+	private List<String> infoTypeList;
+	/**排除指定组队类型,多个用逗号隔开*/
+	private String unlessType;
+
+	private List<String> unlessTypeList;
+
+	public List<String> getUnlessTypeList() {
+		if(StringUtils.isNotEmpty(unlessType)){
+			return Arrays.asList(unlessType.split(","));
+		}
+		return unlessTypeList;
+	}
+
+	public List<String> getInfoTypeList() {
+		if(StringUtils.isNotEmpty(infoType)){
+			return Arrays.asList(infoType.split(","));
+		}
+		return infoTypeList;
+	}
+}

+ 24 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/ExpressCourierNumDTO.java

@@ -0,0 +1,24 @@
+package com.mangoo.rating.recommend.dto;
+
+import lombok.Data;
+
+@Data
+public class ExpressCourierNumDTO {
+
+    private Long id;
+
+    private String courierNum; //快递单号
+
+    private String curierCompany; // 快递公司
+
+    private String curierCompanyCode; // 快递公司编码(快递100--API)
+
+    private String phone;
+
+    public ExpressCourierNumDTO(Long id, String courierNum, String curierCompany, String phone) {
+        this.id = id;
+        this.courierNum = courierNum;
+        this.curierCompany = curierCompany;
+        this.phone = phone;
+    }
+}

+ 18 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/ExpressInfoContextDTO.java

@@ -0,0 +1,18 @@
+package com.mangoo.rating.recommend.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("物流轨迹详情DTO")
+public class ExpressInfoContextDTO {
+
+    @ApiModelProperty(value = "物流时间戳", example = "1714567890000")
+    private Long time;
+
+    @ApiModelProperty(value = "物流描述信息", example = "快件已离开【广州转运中心】,发往【郑州转运中心】")
+    private String desc;
+
+    // getter & setter
+}

+ 11 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/ExpressInfoDTO.java

@@ -0,0 +1,11 @@
+package com.mangoo.rating.recommend.dto;
+
+import lombok.Data;
+
+@Data
+public class ExpressInfoDTO {
+
+    private Integer status;
+
+    private ExpressInfoDetailDTO info;
+}

+ 35 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/ExpressInfoDetailDTO.java

@@ -0,0 +1,35 @@
+package com.mangoo.rating.recommend.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ExpressInfoDetailDTO {
+
+    private Integer status;
+
+    private Integer state;
+
+    private String com;
+
+    private String send_time;
+
+    private List<ExpressInfoContextDTO> context;
+
+    private String latest_progress;
+
+    private String _source_com;
+
+    private String _support_from;
+
+    private Integer isAbstract;
+
+    private String current;
+
+    private Integer currentStatus;
+
+    private String latest_time_new;
+
+    private String latest_time;
+}

+ 166 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/InDto.java

@@ -0,0 +1,166 @@
+package com.mangoo.rating.recommend.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.mangoo.rating.recommend.exception.ServiceException;
+import com.mangoo.rating.recommend.utils.bean.DozerUtils;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 接口入参
+ */
+@Data
+@Slf4j
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class InDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+    //appid
+    private String appid;
+    //openid
+    private String openid;
+    //unionid
+    private String unionid;
+    //用户类型: WX_WEB (微信公众号) WX_APPLET (微信小程序) THIRD (第三方) WX_AUTH(微信授权登陆)
+    private String userType;
+    //userid
+    private Integer userId;
+    //账号
+    private String username;
+    //每页数据条数
+    private Integer pageSize =10;
+    //页码数
+    private Integer pageNo=1;
+    //其他数据
+    private Map<String,Object> data;
+    //部门id
+    private Integer departId;
+
+    private Integer offset;
+
+    private long timestamp;
+
+    private String version;
+    // 极光id
+    private String smsRegisterId;
+    //签名
+    private String sign;
+
+    public Object get(String key){
+        if(null != data && !data.isEmpty()){
+            return this.data.get(key);
+        }
+        return null;
+    }
+    public Object getDefault(String key,Object value){
+        if(null != data && !data.isEmpty()){
+            try{
+                return null == this.data.get(key) ? value : this.data.get(key);
+            }catch (Exception e){
+            }
+        }
+        return value;
+    }
+    public String getString(String key){
+        if(null != data && !data.isEmpty()&&this.data.containsKey(key)){
+            return null != this.data.get(key) ? String.valueOf(this.data.get(key)) : null;
+        }
+        return null;
+    }
+
+    public Boolean existKeyString(String key){
+        if(null != data && !data.isEmpty()&&this.data.containsKey(key)){
+            return true;
+        }
+        return false;
+    }
+
+    public Integer getIntegerParam(String key) {
+        if (CollectionUtils.isEmpty(data)) {
+            return null;
+        }
+        return (Integer) this.data.get(key);
+    }
+
+    public boolean getBooleanParam(String key) {
+        if (CollectionUtils.isEmpty(data)) {
+            return false;
+        }
+        Boolean b = (Boolean) this.data.get(key);
+        return b != null && b;
+    }
+
+    public String getStringDefault(String key,String value){
+        if(null != data && !data.isEmpty()){
+            try {
+                String ret = String.valueOf(this.data.get(key));
+                return StringUtils.isEmpty(ret) ? value : ("null".equals(ret) ? value : ret);
+            }catch (Exception e){}
+        }
+        return value;
+    }
+    public Integer getIntegerDefault(String key,Integer value){
+        if(null != data && !data.isEmpty()){
+            try {
+                Integer ret = (Integer) this.data.get(key);
+                return StringUtils.isEmpty(ret) ? value :  ret;
+            }catch (Exception e){
+            }
+        }
+        return value;
+    }
+    public InDto put(String key ,Object value){
+        if(null == this.data) this.data = new HashMap<>();
+        this.data.put(key, value);
+        return this;
+    }
+    public InDto remove(String key){
+        if(null != this.data && !this.data.isEmpty()){
+            this.data.remove(key);
+        }
+        return this;
+    }
+
+    public Integer getPageSize() {
+        if(this.pageSize == null){
+            return pageSize;
+        }
+        return this.pageSize>0? this.pageSize:10;
+    }
+
+    public Integer getPageNo() {
+        if(this.pageNo == null){
+            return pageNo;
+        }
+        return this.pageNo>0? this.pageNo:1;
+    }
+
+    public Integer getOffset(){
+        if(this.pageNo==null||this.pageSize==null){
+            return 0;
+        }
+        return((pageNo) - 1 ) * pageSize;
+    }
+
+    /**
+     * 转换indto中data为指定参数
+     *
+     * @param param
+     * @param <T>
+     * @return
+     */
+    public <T> T buildParam( T param) {
+        try {
+            DozerUtils.copy(this.data,param);
+            return param;
+        } catch (Exception e) {
+            log.error("参数转换失败:", e);
+        }
+        throw new ServiceException(500, "参数获取失败!");
+    }
+}

+ 194 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/OutDTO.java

@@ -0,0 +1,194 @@
+package com.mangoo.rating.recommend.dto;
+
+import com.mangoo.rating.recommend.constant.RetConstants;
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 输出参数
+ */
+@Data
+@ToString
+public class OutDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+    private boolean success = true;
+
+    private Integer code = 0;
+
+    private String msg = "成功";
+
+    private Map<String ,Object> data;
+    //时间戳
+    private long timestamp = System.currentTimeMillis();
+    //数据总量
+    private Integer total;
+    //每页条数
+    private Integer pageSize = 10;
+    //页码
+    private Integer pageNo = 1;
+    //总页数
+    private Integer pages;
+    //
+    private String roleCode;
+
+    public OutDTO(){}
+
+    public OutDTO(boolean success, Integer code, String msg){
+        this.success =success;
+        this.code = code;
+        this.msg = msg;
+    }
+
+    /**
+     *
+     * @param pageSize 每页条数
+     * @param pageNo 页码
+     * @param total 总量
+     * @return
+     */
+    public OutDTO setCount(Integer pageSize ,Integer pageNo ,Integer total){
+        this.total = total;
+        this.pageSize = pageSize;
+        this.pageNo = pageNo;
+        if(total % pageSize > 0){
+            this.pages = total / pageSize + 1;
+        }else{
+            this.pages = total / pageSize;
+        }
+        return this;
+    }
+
+    /**
+     * @param inDto
+     * @param total
+     * 设置分页
+     * @return
+     */
+    public OutDTO setCount(InDto inDto,Integer total){
+        this.total = total;
+        this.pageSize = inDto.getPageSize();
+        this.pageNo = inDto.getPageNo();
+        if(total % this.pageSize > 0){
+            this.pages = total / this.pageSize + 1;
+        }else{
+            this.pages = total / this.pageSize;
+        }
+        return this;
+    }
+
+    public OutDTO page(InDto inDto,Integer total , String key, List datas){
+        OutDTO outDTO = this.setCount(inDto, total);
+        if(null != datas && datas.size() > 0 ){
+            if(datas.size() >= inDto.getPageSize() * inDto.getPageNo()){
+                datas = datas.subList(inDto.getPageSize() *(inDto.getPageNo()-1),inDto.getPageSize() *inDto.getPageNo() );
+            }else{
+                datas = datas.subList(inDto.getPageSize() *(inDto.getPageNo()-1),datas.size());
+            }
+        }
+        outDTO.put(key,datas);
+        return outDTO;
+    }
+
+    public OutDTO buildPage(InDto inDto,Integer total , String key, List datas){
+        OutDTO outDTO = this.setCount(inDto, total);
+        outDTO.put(key,datas);
+        return outDTO;
+    }
+
+    /**
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public OutDTO put(String key,Object value){
+        if(null == this.data) this.data = new HashMap<>();
+        this.data.put(key,value);
+        return this;
+    }
+
+    /**
+     *
+     * @return
+     */
+    public OutDTO putAll(Map<String, Object> map){
+        if(null == this.data) this.data = new HashMap<>();
+        this.data.putAll(map);
+        return this;
+    }
+
+    /**
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public OutDTO putString(String key,String value){
+        if(null == this.data) this.data = new HashMap<>();
+        this.data.put(key,value);
+        return this;
+    }
+
+    /**
+     *
+     * @param key
+     * @return
+     */
+    public Object get(String key){
+        if(null != this.data) return this.data.get(key);
+        return null;
+    }
+
+    /**
+     * get String
+     * @param key
+     * @return
+     */
+    public String getString(String key){
+        if(null != this.data)  return (String) this.data.get(key);
+        return null;
+    }
+
+    public static OutDTO ok(){
+        OutDTO outDTO = new OutDTO();
+        outDTO.put("ok","");
+        return outDTO;
+    }
+
+    public static OutDTO ok(String msg) {
+        return new OutDTO(true,0,msg);
+    }
+
+    public static OutDTO ok(Integer code ,String msg){
+        return new OutDTO(true,code,msg);
+    }
+
+    /**
+     * 失败
+     * @param code
+     * @param msg
+     * @return
+     */
+    public static OutDTO error(Integer code,String msg){
+        return new OutDTO(false,code,msg);
+    }
+
+    /**
+     *
+     * @param message
+     * @return
+     */
+    public static OutDTO error500(String message) {
+        return new OutDTO(false,500,message);
+    }
+
+
+    public static OutDTO errorRet(RetConstants retConstants) {
+        return new OutDTO(false, retConstants.getCode(), retConstants.getMessage());
+    }
+}

+ 103 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/ReceiveInfoDTO.java

@@ -0,0 +1,103 @@
+package com.mangoo.rating.recommend.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+
+/**
+ *
+ */
+@Data
+@ApiModel(value = "快递信息【下单】收件信息")
+@AllArgsConstructor
+@NoArgsConstructor
+public class ReceiveInfoDTO {
+
+    /**
+     * 物品信息
+     */
+    @ApiModelProperty(value = "物品信息",required = true)
+    @NotBlank(message = "物品信息不能为空")
+    private String goods;
+    /**
+     * 商户系统内部子订单号
+     */
+    @ApiModelProperty(value = "商户系统内部子订单号",required = true)
+    @NotBlank(message = "商户系统内部子订单号不能为空")
+    private String subThirdOrderNo;
+    /**
+     * 收件人手机号
+     */
+    @ApiModelProperty(value = "收件人手机号",required = true)
+    @NotBlank(message = "收件人手机号不能为空")
+    private String receivePhone;
+    /**
+     * 收件人姓名
+     */
+    @ApiModelProperty(value = "收件人姓名",required = true)
+    @NotBlank(message = "收件人姓名不能为空")
+    private String receiveName;
+    /**
+     * 收件人地址
+     */
+    @ApiModelProperty(value = "收件人地址",required = true)
+    @NotBlank(message = "收件人地址不能为空")
+    private String receiveAddress;
+    /**
+     * 下单重量
+     */
+    @ApiModelProperty(value = "下单重量",required = true)
+    @NotNull(message = "下单重量不能为空")
+    private Integer weight;
+    /**
+     * 包裹数
+     */
+    @ApiModelProperty(value = "包裹数",required = true)
+    @NotNull(message = "包裹数不能为空")
+    private Integer packageNum;//包裹数
+    /**
+     * 保价金额(元)
+     */
+    @ApiModelProperty(value = "保价金额(元)")
+    private Integer guaranteeValueAmount;
+    /**
+     * 长(厘米),当type=17【中通快运】时,必传
+     */
+    @ApiModelProperty(value = "长(厘米)【中通快运】时,必传")
+    private Integer length;
+    /**
+     * 宽(厘米),当type=17【中通快运】时,必传
+     */
+    @ApiModelProperty(value = "宽(厘米)【中通快运】时,必传")
+    private Integer width;
+    /**
+     * 高(厘米),当type=17【中通快运】时,必传
+     */
+    @ApiModelProperty(value = "高(厘米)【中通快运】时,必传")
+    private Integer height;
+    /**
+     * 运单备注
+     */
+    @ApiModelProperty(value = "运单备注")
+    private String remark;
+    /**
+     * 总体积m³ 和长宽高换算的体积做比较,使用较大的值 中通快运、百世快运、顺心捷达强制强制输入
+     */
+    @ApiModelProperty(value = "总体积m³ 和长宽高换算的体积做比较,使用较大的值")
+    private BigDecimal volume;
+    /**
+     * 收件信息
+     */
+    public ReceiveInfoDTO(String subThirdOrderNo, String receiveName, String receivePhone, String receiveAddress) {
+        this.subThirdOrderNo = subThirdOrderNo;
+        this.receiveName = receiveName;
+        this.receivePhone = receivePhone;
+        this.receiveAddress = receiveAddress;
+    }
+}

+ 40 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/RecognizedInfoQueryDTO.java

@@ -0,0 +1,40 @@
+package com.mangoo.rating.recommend.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class RecognizedInfoQueryDTO {
+
+
+    @ApiModelProperty(value = "卡ID")
+    private Long cardId;
+
+    @ApiModelProperty(value = "订单编号")
+    private String orderNo;
+
+    @ApiModelProperty(value = "球员")
+    private String player;
+
+    @ApiModelProperty(value = "年份")
+    private String year;
+
+    @ApiModelProperty(value = "系列")
+    private String series;
+
+    @ApiModelProperty(value = "卡种")
+    private String cardSet;
+
+    @ApiModelProperty(value = "卡密编号")
+    private String confidentialId;
+
+    @ApiModelProperty(value = "限编")
+    private String limitId;
+
+}

+ 47 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/TaskResultDTO.java

@@ -0,0 +1,47 @@
+package com.mangoo.rating.recommend.dto;
+
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+@Data
+@ToString
+public class TaskResultDTO implements Serializable {
+	private static final long serializableUID = 1L;
+
+	/**
+	 * 状态:uploaded 已上传
+	 */
+	private String status;
+
+	/**
+	 * 摄像头ID
+	 */
+	private String camera_id;
+
+	/**
+	 * 任务名称
+	 */
+	private String task_name;
+
+	/**
+	 * 开始时间
+	 */
+	private String start_time;
+
+	/**
+	 * 视频地址
+	 */
+	private String video_url;
+
+	/**
+	 * 停止原因
+	 */
+	private String stop_reason;
+
+	/**
+	 * 本地文件是否已删除
+	 */
+	private Boolean local_file_deleted;
+}

+ 54 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/UserInfo.java

@@ -0,0 +1,54 @@
+package com.mangoo.rating.recommend.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author by po'yi
+ * @Classname UserInfo
+ * @Description 用户信息
+ * @Date 2022/8/18 15:46
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class UserInfo implements Serializable {
+	/**头像*/
+	private String avatar;
+
+	private Integer merchantId;
+	private String merchantName;
+	private String merchantAvatar;
+
+	/**
+	 * 用户唯一标识(sub 字段)
+	 */
+	private String sub;
+
+	/**
+	 * 受众标识(aud 字段,如 client)
+	 */
+	private String aud;
+
+	/**
+	 * 用户昵称
+	 */
+	private String nickname;
+
+	/**
+	 * 角色编码列表
+	 */
+	private List<String> roleCodeList;
+
+	/**
+	 * 用户ID
+	 */
+	private Long id;
+
+	/**
+	 * 用户账号(登录名)
+	 */
+	private String account;
+}

+ 23 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/card/CardCameraCacheDTO.java

@@ -0,0 +1,23 @@
+package com.mangoo.rating.recommend.dto.card;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CardCameraCacheDTO {
+
+    /**
+     * 订单编号
+     */
+    private String orderNo;
+
+    /**
+     * 摄像头编号
+     */
+    private String camNo;
+
+}
+

+ 28 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/dto/card/CardCreateDTO.java

@@ -0,0 +1,28 @@
+package com.mangoo.rating.recommend.dto.card;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel(description = "卡片信息保存参数")
+public class CardCreateDTO {
+
+    /**
+     *  评级款式
+     */
+    @ApiModelProperty(value = "评级款式(枚举:1-普通,2-快速,3-闪评)")
+    private Integer cardStyle;
+
+    /**
+     *  评级时效
+     */
+    @ApiModelProperty(value = "评级时效(枚举:1-普通,2-快速,3-闪评)")
+    private Integer evaluateEfficiency;
+
+}
+

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