feat(product): 添加商品管理功能模块

- 新增商品控制器ProductController,提供商品创建、查询、图片上传接口
- 实现商品详情页URL生成逻辑
- 添加商品图片单文件及批量上传功能,支持多种图片格式校验
- 集成OSS服务实现文件存储
- 新增商品响应DTO,包含商品基本信息、SKU列表及状态信息
- 定义商品服务接口及实现类,封装商品核心业务逻辑
- 添加商品状态枚举及SKU状态枚举定义
- 实现商品数据访问层基础结构
This commit is contained in:
2025-12-22 09:20:16 +08:00
parent 3133369053
commit 2d00e72637
8 changed files with 936 additions and 0 deletions

View File

@@ -0,0 +1,144 @@
2025-12-19 18:34:38.656 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getTypeForFactoryBeanFromAttributes(FactoryBeanRegistrySupport.java:86)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:838)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:620)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:573)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:532)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:138)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:323)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50)
2025-12-19 18:34:38.658 [restartedMain] ERROR com.mtkj.mtpay.MtPayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getTypeForFactoryBeanFromAttributes(FactoryBeanRegistrySupport.java:86)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:838)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:620)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:573)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:532)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:138)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:323)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50)
2025-12-19 18:34:38.661 [main] ERROR com.mtkj.mtpay.MtPayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException: null
at org.springframework.boot.devtools.restart.SilentExitExceptionHandler.exitCurrentThread(SilentExitExceptionHandler.java:92)
at org.springframework.boot.devtools.restart.Restarter.immediateRestart(Restarter.java:179)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:163)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:532)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:98)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:51)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:149)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:137)
at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:54)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)
2025-12-19 18:34:49.156 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getTypeForFactoryBeanFromAttributes(FactoryBeanRegistrySupport.java:86)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:838)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:620)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:573)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:532)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:138)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:323)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50)
2025-12-19 18:34:49.158 [restartedMain] ERROR com.mtkj.mtpay.MtPayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getTypeForFactoryBeanFromAttributes(FactoryBeanRegistrySupport.java:86)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:838)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:620)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:573)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:532)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:138)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:323)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50)
2025-12-19 18:34:49.161 [main] ERROR com.mtkj.mtpay.MtPayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException: null
at org.springframework.boot.devtools.restart.SilentExitExceptionHandler.exitCurrentThread(SilentExitExceptionHandler.java:92)
at org.springframework.boot.devtools.restart.Restarter.immediateRestart(Restarter.java:179)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:163)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:532)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:98)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:51)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:149)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:137)
at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:54)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)

View File

@@ -0,0 +1,72 @@
2025-12-22 09:18:35.818 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getTypeForFactoryBeanFromAttributes(FactoryBeanRegistrySupport.java:86)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:838)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:620)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:573)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:532)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:138)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:323)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50)
2025-12-22 09:18:35.820 [restartedMain] ERROR com.mtkj.mtpay.MtPayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getTypeForFactoryBeanFromAttributes(FactoryBeanRegistrySupport.java:86)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:838)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:620)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:573)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:532)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:138)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:323)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50)
2025-12-22 09:18:35.825 [main] ERROR com.mtkj.mtpay.MtPayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException: null
at org.springframework.boot.devtools.restart.SilentExitExceptionHandler.exitCurrentThread(SilentExitExceptionHandler.java:92)
at org.springframework.boot.devtools.restart.Restarter.immediateRestart(Restarter.java:179)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:163)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:532)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:98)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:51)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:149)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:137)
at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:54)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)

View File

@@ -0,0 +1,76 @@
2025-12-22 09:18:34.910 [background-preinit] INFO org.hibernate.validator.internal.util.Version - HV000001: Hibernate Validator 8.0.1.Final
2025-12-22 09:18:34.954 [restartedMain] INFO com.mtkj.mtpay.MtPayApplication - Starting MtPayApplication using Java 17.0.12 with PID 19392 (E:\MTKJPAY\mt-pay\target\classes started by 18969 in E:\MTKJPAY)
2025-12-22 09:18:34.955 [restartedMain] INFO com.mtkj.mtpay.MtPayApplication - No active profile set, falling back to 1 default profile: "default"
2025-12-22 09:18:35.783 [restartedMain] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
2025-12-22 09:18:35.818 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getTypeForFactoryBeanFromAttributes(FactoryBeanRegistrySupport.java:86)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:838)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:620)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:573)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:532)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:138)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:323)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50)
2025-12-22 09:18:35.820 [restartedMain] ERROR com.mtkj.mtpay.MtPayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getTypeForFactoryBeanFromAttributes(FactoryBeanRegistrySupport.java:86)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:838)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:620)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:573)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:532)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:138)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:323)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50)
2025-12-22 09:18:35.825 [main] ERROR com.mtkj.mtpay.MtPayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException: null
at org.springframework.boot.devtools.restart.SilentExitExceptionHandler.exitCurrentThread(SilentExitExceptionHandler.java:92)
at org.springframework.boot.devtools.restart.Restarter.immediateRestart(Restarter.java:179)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:163)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:532)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:98)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:51)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:149)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:137)
at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:54)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at com.mtkj.mtpay.MtPayApplication.main(MtPayApplication.java:24)

View File

@@ -0,0 +1,48 @@
package com.mtkj.mtpay.common.enums;
/**
* 商品状态枚举
*/
public enum ProductStatus {
/**
* 上架
*/
ACTIVE("ACTIVE", "上架"),
/**
* 下架
*/
INACTIVE("INACTIVE", "下架"),
/**
* 已删除
*/
DELETED("DELETED", "已删除");
private final String code;
private final String description;
ProductStatus(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
public static ProductStatus fromCode(String code) {
for (ProductStatus status : values()) {
if (status.code.equals(code)) {
return status;
}
}
return null;
}
}

View File

@@ -0,0 +1,206 @@
package com.mtkj.mtpay.controller;
import com.mtkj.mtpay.common.Result;
import com.mtkj.mtpay.dto.request.CreateProductRequestDTO;
import com.mtkj.mtpay.dto.response.ProductResponseDTO;
import com.mtkj.mtpay.service.ProductService;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 商品管理控制器
*/
@Slf4j
@RestController
@RequestMapping("/api/product")
public class ProductController {
@Autowired
private ProductService productService;
@Autowired
private com.mtkj.mtpay.service.OssService ossService;
/**
* 创建商品
*/
@PostMapping
public Result<ProductResponseDTO> createProduct(@Valid @RequestBody CreateProductRequestDTO request) {
log.info("创建商品请求:{}", request);
ProductResponseDTO product = productService.createProduct(request);
return Result.success("商品创建成功", product);
}
/**
* 获取商品详情
*/
@GetMapping("/{id}")
public Result<ProductResponseDTO> getProduct(@PathVariable Long id) {
log.info("获取商品详情商品ID{}", id);
ProductResponseDTO product = productService.getProductById(id);
return Result.success(product);
}
/**
* 获取商品详情页URL
*/
@GetMapping("/{id}/url")
public Result<Map<String, String>> getProductUrl(@PathVariable Long id) {
log.info("获取商品URL商品ID{}", id);
String url = productService.getProductUrl(id);
Map<String, String> result = new HashMap<>();
result.put("url", url);
return Result.success(result);
}
/**
* 上传商品图片单文件上传参考mt-admin的CommonController实现
*/
@PostMapping("/upload/image")
public Result<Map<String, Object>> uploadImage(@RequestParam("file") MultipartFile file) {
try {
if (file == null || file.isEmpty()) {
return Result.fail("文件不能为空");
}
String originalFilename = file.getOriginalFilename();
if (originalFilename == null) {
return Result.fail("文件名不能为空");
}
// 验证文件类型
String extension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
if (!isImageFile(extension)) {
return Result.fail("只支持图片文件jpg、jpeg、png、gif、webp、bmp");
}
// 验证文件大小最大10MB
if (file.getSize() > 10 * 1024 * 1024) {
return Result.fail("文件大小不能超过10MB");
}
// 上传到OSS参考mt-admin实现
byte[] content = file.getBytes();
String imageUrl = ossService.upload(content, originalFilename);
Map<String, Object> result = new HashMap<>();
result.put("url", imageUrl);
result.put("fileName", imageUrl);
result.put("originalFilename", originalFilename);
log.info("图片上传成功,文件名: {}, URL: {}", originalFilename, imageUrl);
return Result.success("图片上传成功", result);
} catch (Exception e) {
log.error("图片上传失败", e);
return Result.fail("图片上传失败:" + e.getMessage());
}
}
/**
* 批量上传商品图片(多文件上传)
*/
@PostMapping("/upload/images")
public Result<Map<String, Object>> uploadImages(@RequestParam("file") MultipartFile[] files) {
if (files == null || files.length == 0) {
return Result.fail("文件不能为空");
}
try {
List<Map<String, String>> uploadedFiles = new ArrayList<>();
List<String> errors = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
MultipartFile uploadFile = files[i];
if (uploadFile == null || uploadFile.isEmpty()) {
errors.add("" + (i + 1) + "个文件为空");
continue;
}
// 验证文件类型
String originalFilename = uploadFile.getOriginalFilename();
if (originalFilename == null) {
errors.add("" + (i + 1) + "个文件名不能为空");
continue;
}
String extension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
if (!isImageFile(extension)) {
errors.add("" + (i + 1) + "个文件不是图片格式(" + originalFilename + "");
continue;
}
// 验证文件大小最大10MB
if (uploadFile.getSize() > 10 * 1024 * 1024) {
errors.add("" + (i + 1) + "个文件大小超过10MB" + originalFilename + "");
continue;
}
try {
// 上传到OSS
byte[] content = uploadFile.getBytes();
String imageUrl = ossService.upload(content, originalFilename);
Map<String, String> fileInfo = new HashMap<>();
fileInfo.put("url", imageUrl);
fileInfo.put("originalFilename", originalFilename);
fileInfo.put("size", String.valueOf(uploadFile.getSize()));
uploadedFiles.add(fileInfo);
log.info("图片上传成功,文件名: {}, URL: {}", originalFilename, imageUrl);
} catch (Exception e) {
log.error("上传第{}个文件失败: {}", i + 1, originalFilename, e);
errors.add("" + (i + 1) + "个文件上传失败: " + e.getMessage());
}
}
Map<String, Object> result = new HashMap<>();
result.put("files", uploadedFiles);
result.put("successCount", uploadedFiles.size());
result.put("totalCount", files.length);
if (!errors.isEmpty()) {
result.put("errors", errors);
}
if (uploadedFiles.isEmpty()) {
return Result.fail("所有文件上传失败: " + String.join("; ", errors));
}
String message = String.format("成功上传%d/%d个文件", uploadedFiles.size(), files.length);
if (!errors.isEmpty()) {
message += ",部分文件上传失败";
}
log.info("批量图片上传完成,成功: {}/{}, 失败: {}", uploadedFiles.size(), files.length, errors.size());
return Result.success(message, result);
} catch (Exception e) {
log.error("批量图片上传失败", e);
return Result.fail("图片上传失败:" + e.getMessage());
}
}
/**
* 判断是否为图片文件
*/
private boolean isImageFile(String extension) {
String[] imageExtensions = {"jpg", "jpeg", "png", "gif", "webp", "bmp"};
for (String ext : imageExtensions) {
if (ext.equalsIgnoreCase(extension)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,143 @@
package com.mtkj.mtpay.dto.response;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* 商品响应DTO
*/
@Data
public class ProductResponseDTO implements Serializable {
/**
* 商品ID
*/
private Long id;
/**
* 商品名称
*/
private String name;
/**
* 商品价格(基础价格)
*/
private BigDecimal price;
/**
* 主图URL单个兼容旧版本取mainImages的第一个
*/
private String mainImage;
/**
* 主图URL列表支持多张主图
*/
private List<String> mainImages;
/**
* 商品状态
*/
private String status;
/**
* 店铺ID
*/
private Long shopId;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* SKU列表
*/
private List<ProductSkuResponseDTO> skus;
/**
* SKU响应DTO
*/
@Data
public static class ProductSkuResponseDTO implements Serializable {
/**
* SKU ID
*/
private Long id;
/**
* 商品ID
*/
private Long productId;
/**
* SKU编码
*/
private String sku;
/**
* 价格
*/
private BigDecimal price;
/**
* 货币
*/
private String currency;
/**
* 库存数量
*/
private Integer stock;
/**
* 销售属性JSON格式
*/
private String salesAttrs;
/**
* SKU图片URL
*/
private String skuImage;
/**
* 重量
*/
private BigDecimal weight;
/**
* 大小/尺寸JSON格式
*/
private String size;
/**
* 规格
*/
private String specification;
/**
* SKU状态
*/
private String status;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}
}

View File

@@ -0,0 +1,32 @@
package com.mtkj.mtpay.service;
import com.mtkj.mtpay.dto.request.CreateProductRequestDTO;
import com.mtkj.mtpay.dto.response.ProductResponseDTO;
/**
* 商品服务接口
*/
public interface ProductService {
/**
* 创建商品
* @param request 创建商品请求
* @return 商品响应
*/
ProductResponseDTO createProduct(CreateProductRequestDTO request);
/**
* 根据ID获取商品详情
* @param id 商品ID
* @return 商品响应
*/
ProductResponseDTO getProductById(Long id);
/**
* 获取商品详情页URL
* @param id 商品ID
* @return 商品详情页URL
*/
String getProductUrl(Long id);
}

View File

@@ -0,0 +1,215 @@
package com.mtkj.mtpay.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mtkj.mtpay.common.ResultCode;
import com.mtkj.mtpay.common.enums.ProductStatus;
import com.mtkj.mtpay.common.enums.SkuStatus;
import com.mtkj.mtpay.dto.request.CreateProductRequestDTO;
import com.mtkj.mtpay.dto.response.ProductResponseDTO;
import com.mtkj.mtpay.entity.MtProduct;
import com.mtkj.mtpay.entity.MtProductSku;
import com.mtkj.mtpay.exception.BusinessException;
import com.mtkj.mtpay.mapper.MtProductMapper;
import com.mtkj.mtpay.mapper.MtProductSkuMapper;
import com.mtkj.mtpay.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 商品服务实现类
*/
@Slf4j
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private MtProductMapper productMapper;
@Autowired
private MtProductSkuMapper productSkuMapper;
@Autowired
private ObjectMapper objectMapper;
@Value("${server.port:8082}")
private String serverPort;
@Value("${server.servlet.context-path:}")
private String contextPath;
/**
* 前端访问地址用于生成商品详情页URL
*/
@Value("${app.frontend.url:http://localhost:3000}")
private String frontendUrl;
@Override
@Transactional(rollbackFor = Exception.class)
public ProductResponseDTO createProduct(CreateProductRequestDTO request) {
log.info("开始创建商品,商品名称: {}, 店铺ID: {}, SKU数量: {}",
request.getName(), request.getShopId(),
request.getSkus() != null ? request.getSkus().size() : 0);
// 创建商品
MtProduct product = new MtProduct();
product.setName(request.getName());
product.setPrice(request.getPrice());
// 处理主图优先使用mainImages多图如果没有则使用mainImage单图兼容旧版本
String mainImageValue = null;
if (request.getMainImages() != null && !request.getMainImages().isEmpty()) {
// 多个主图转换为JSON数组存储
try {
mainImageValue = objectMapper.writeValueAsString(request.getMainImages());
log.debug("商品主图(多图): {}", mainImageValue);
} catch (Exception e) {
log.error("转换主图列表为JSON失败", e);
throw new BusinessException(ResultCode.SYSTEM_ERROR, "主图数据格式错误");
}
} else if (StringUtils.hasText(request.getMainImage())) {
// 单个主图,兼容旧版本
mainImageValue = request.getMainImage();
log.debug("商品主图(单图): {}", mainImageValue);
}
product.setMainImage(mainImageValue);
product.setStatus(request.getStatus() != null ? request.getStatus() : ProductStatus.ACTIVE.getCode());
product.setShopId(request.getShopId());
int result = productMapper.insert(product);
if (result <= 0) {
log.error("创建商品失败,商品名称: {}", request.getName());
throw new BusinessException(ResultCode.SYSTEM_ERROR, "创建商品失败");
}
log.debug("商品基础信息插入成功商品ID: {}", product.getId());
// 创建SKU
if (request.getSkus() != null && !request.getSkus().isEmpty()) {
log.debug("开始创建SKU数量: {}", request.getSkus().size());
for (CreateProductRequestDTO.CreateProductSkuDTO skuDTO : request.getSkus()) {
MtProductSku sku = new MtProductSku();
sku.setProductId(product.getId());
sku.setSku(skuDTO.getSku());
sku.setPrice(skuDTO.getPrice());
sku.setCurrency(skuDTO.getCurrency());
sku.setStock(skuDTO.getStock());
sku.setSalesAttrs(skuDTO.getSalesAttrs());
sku.setSkuImage(skuDTO.getSkuImage());
sku.setWeight(skuDTO.getWeight());
sku.setSize(skuDTO.getSize());
sku.setSpecification(skuDTO.getSpecification());
sku.setStatus(skuDTO.getStatus() != null ? skuDTO.getStatus() : SkuStatus.ACTIVE.getCode());
int skuResult = productSkuMapper.insert(sku);
if (skuResult <= 0) {
log.error("创建SKU失败商品ID: {}, SKU编码: {}", product.getId(), skuDTO.getSku());
throw new BusinessException(ResultCode.SYSTEM_ERROR, "创建SKU失败");
}
log.debug("SKU创建成功商品ID: {}, SKU编码: {}, SKU ID: {}",
product.getId(), skuDTO.getSku(), sku.getId());
}
}
log.info("商品创建成功商品ID: {}, 商品名称: {}, SKU数量: {}",
product.getId(), product.getName(),
request.getSkus() != null ? request.getSkus().size() : 0);
// 返回商品详情
return getProductById(product.getId());
}
@Override
public ProductResponseDTO getProductById(Long id) {
log.debug("查询商品详情商品ID: {}", id);
MtProduct product = productMapper.selectById(id);
if (product == null) {
log.warn("商品不存在商品ID: {}", id);
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "商品不存在");
}
// 查询SKU列表
LambdaQueryWrapper<MtProductSku> skuWrapper = new LambdaQueryWrapper<>();
skuWrapper.eq(MtProductSku::getProductId, id);
skuWrapper.eq(MtProductSku::getStatus, SkuStatus.ACTIVE.getCode());
List<MtProductSku> skus = productSkuMapper.selectList(skuWrapper);
log.debug("查询到商品SKU数量: {}, 商品ID: {}", skus.size(), id);
// 转换为响应DTO
ProductResponseDTO response = new ProductResponseDTO();
BeanUtils.copyProperties(product, response);
// 处理主图解析JSON数组或使用单个URL
if (StringUtils.hasText(product.getMainImage())) {
try {
// 尝试解析为JSON数组
if (product.getMainImage().trim().startsWith("[")) {
List<String> mainImages = objectMapper.readValue(
product.getMainImage(),
new TypeReference<List<String>>() {}
);
response.setMainImages(mainImages);
// 兼容第一个作为mainImage
if (!mainImages.isEmpty()) {
response.setMainImage(mainImages.get(0));
}
log.debug("解析商品主图(多图),数量: {}", mainImages.size());
} else {
// 单个URL
response.setMainImage(product.getMainImage());
List<String> singleImageList = new ArrayList<>();
singleImageList.add(product.getMainImage());
response.setMainImages(singleImageList);
log.debug("解析商品主图(单图)");
}
} catch (Exception e) {
log.warn("解析商品主图失败使用原始值商品ID: {}", id, e);
response.setMainImage(product.getMainImage());
List<String> singleImageList = new ArrayList<>();
singleImageList.add(product.getMainImage());
response.setMainImages(singleImageList);
}
}
List<ProductResponseDTO.ProductSkuResponseDTO> skuDTOs = skus.stream().map(sku -> {
ProductResponseDTO.ProductSkuResponseDTO skuDTO = new ProductResponseDTO.ProductSkuResponseDTO();
BeanUtils.copyProperties(sku, skuDTO);
return skuDTO;
}).collect(Collectors.toList());
response.setSkus(skuDTOs);
log.info("获取商品详情成功商品ID: {}, 商品名称: {}, SKU数量: {}, 主图数量: {}",
id, product.getName(), skuDTOs.size(),
response.getMainImages() != null ? response.getMainImages().size() : 0);
return response;
}
@Override
public String getProductUrl(Long id) {
log.debug("获取商品URL商品ID: {}", id);
// 检查商品是否存在
MtProduct product = productMapper.selectById(id);
if (product == null) {
log.warn("商品不存在无法生成URL商品ID: {}", id);
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "商品不存在");
}
// 构建商品详情页URL使用前端地址
// 格式http://前端地址/product/{id}
String productUrl = frontendUrl + "/product/" + id;
log.info("生成商品URL成功商品ID: {}, URL: {}", id, productUrl);
return productUrl;
}
}