Răsfoiți Sursa

first commit

1 săptămână în urmă
comite
78f70d258a
37 a modificat fișierele cu 1042 adăugiri și 0 ștergeri
  1. 38 0
      .gitignore
  2. 0 0
      .idea/.gitignore
  3. 7 0
      .idea/encodings.xml
  4. 14 0
      .idea/misc.xml
  5. 6 0
      .idea/vcs.xml
  6. 109 0
      .idea/workspace.xml
  7. 111 0
      pom.xml
  8. 11 0
      src/main/java/conm/zhentao/Application.java
  9. 26 0
      src/main/java/conm/zhentao/config/SecurityConfig.java
  10. 25 0
      src/main/java/conm/zhentao/controller/AuthController.java
  11. 45 0
      src/main/java/conm/zhentao/controller/OrderController.java
  12. 13 0
      src/main/java/conm/zhentao/dto/LoginRequest.java
  13. 16 0
      src/main/java/conm/zhentao/dto/LoginResponse.java
  14. 17 0
      src/main/java/conm/zhentao/dto/OrderCreateDTO.java
  15. 15 0
      src/main/java/conm/zhentao/dto/OrderItemDTO.java
  16. 24 0
      src/main/java/conm/zhentao/entity/OrderDetail.java
  17. 25 0
      src/main/java/conm/zhentao/entity/Order_info.java
  18. 17 0
      src/main/java/conm/zhentao/entity/Sku.java
  19. 21 0
      src/main/java/conm/zhentao/entity/User.java
  20. 18 0
      src/main/java/conm/zhentao/exception/BusinessException.java
  21. 57 0
      src/main/java/conm/zhentao/exception/GlobalExceptionHandler.java
  22. 9 0
      src/main/java/conm/zhentao/mapper/OrderDetailMapper.java
  23. 9 0
      src/main/java/conm/zhentao/mapper/OrderMapper.java
  24. 9 0
      src/main/java/conm/zhentao/mapper/SkuMapper.java
  25. 9 0
      src/main/java/conm/zhentao/mapper/UserMapper.java
  26. 8 0
      src/main/java/conm/zhentao/service/CouponService.java
  27. 9 0
      src/main/java/conm/zhentao/service/OrderService.java
  28. 8 0
      src/main/java/conm/zhentao/service/SkuService.java
  29. 11 0
      src/main/java/conm/zhentao/service/UserService.java
  30. 26 0
      src/main/java/conm/zhentao/service/impl/CouponServiceImpl.java
  31. 122 0
      src/main/java/conm/zhentao/service/impl/OrderServiceImpl.java
  32. 12 0
      src/main/java/conm/zhentao/service/impl/SkuServiceImpl.java
  33. 55 0
      src/main/java/conm/zhentao/service/impl/UserServiceImpl.java
  34. 21 0
      src/main/java/conm/zhentao/util/JwtUtil.java
  35. 20 0
      src/main/java/conm/zhentao/util/RedisUtil.java
  36. 72 0
      src/main/java/conm/zhentao/util/TokenUtil.java
  37. 27 0
      src/main/resources/application.yml

+ 38 - 0
.gitignore

@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store

+ 0 - 0
.idea/.gitignore


+ 7 - 0
.idea/encodings.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
+  </component>
+</project>

+ 14 - 0
.idea/misc.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="MavenProjectsManager">
+    <option name="originalFiles">
+      <list>
+        <option value="$PROJECT_DIR$/pom.xml" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 109 - 0
.idea/workspace.xml

@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AutoImportSettings">
+    <option name="autoReloadType" value="SELECTIVE" />
+  </component>
+  <component name="ChangeListManager">
+    <list default="true" id="d0601eaf-b5ad-495a-9532-b47a8f8263d8" name="更改" comment="">
+      <change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/src/main/java/conm/zhentao/dto/LoginRequest.java" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/src/main/java/conm/zhentao/dto/LoginResponse.java" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/src/main/java/conm/zhentao/dto/OrderCreateDTO.java" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/src/main/java/conm/zhentao/dto/OrderItemDTO.java" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/src/main/java/conm/zhentao/exception/BusinessException.java" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/src/main/java/conm/zhentao/exception/GlobalExceptionHandler.java" afterDir="false" />
+    </list>
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="FileTemplateManagerImpl">
+    <option name="RECENT_TEMPLATES">
+      <list>
+        <option value="applicatio" />
+        <option value="Class" />
+      </list>
+    </option>
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
+  </component>
+  <component name="MarkdownSettingsMigration">
+    <option name="stateVersion" value="1" />
+  </component>
+  <component name="MavenImportPreferences">
+    <option name="generalSettings">
+      <MavenGeneralSettings>
+        <option name="localRepository" value="D:\apache-maven-3.9.2\MAVEN" />
+        <option name="userSettingsFile" value="D:\apache-maven-3.9.2\conf\settings.xml" />
+      </MavenGeneralSettings>
+    </option>
+    <option name="importingSettings">
+      <MavenImportingSettings>
+        <option name="workspaceImportEnabled" value="true" />
+      </MavenImportingSettings>
+    </option>
+  </component>
+  <component name="ProjectId" id="2wt1QzvMsP0fBILm6za1tv6Rifs" />
+  <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
+  <component name="ProjectViewState">
+    <option name="autoscrollFromSource" value="true" />
+    <option name="hideEmptyMiddlePackages" value="true" />
+    <option name="showLibraryContents" value="true" />
+  </component>
+  <component name="PropertiesComponent"><![CDATA[{
+  "keyToString": {
+    "RunOnceActivity.OpenProjectViewOnStart": "true",
+    "RunOnceActivity.ShowReadmeOnStart": "true",
+    "WebServerToolWindowFactoryState": "false",
+    "last_opened_file_path": "D:/Intell/专高四2/untitled789",
+    "node.js.detected.package.eslint": "true",
+    "node.js.detected.package.tslint": "true",
+    "node.js.selected.package.eslint": "(autodetect)",
+    "node.js.selected.package.tslint": "(autodetect)",
+    "settings.editor.selected.configurable": "MavenSettings",
+    "vue.rearranger.settings.migration": "true"
+  }
+}]]></component>
+  <component name="RecentsManager">
+    <key name="CopyFile.RECENT_KEYS">
+      <recent name="D:\Intell\专高四2\mazhongjunsale\src\main\java\conm\zhentao\mapper" />
+      <recent name="D:\Intell\专高四2\mazhongjunsale\src\main\java\conm\zhentao\entity" />
+      <recent name="D:\Intell\专高四2\mazhongjunsale\src\main\java\conm\zhentao\service" />
+      <recent name="D:\Intell\专高四2\mazhongjunsale\src\main\java\conm\zhentao\util" />
+      <recent name="D:\Intell\专高四2\mazhongjunsale\src\main\java\conm\zhentao\exception" />
+    </key>
+  </component>
+  <component name="RunManager">
+    <configuration default="true" type="JetRunConfigurationType">
+      <module name="mazhongjunsale" />
+      <method v="2">
+        <option name="Make" enabled="true" />
+      </method>
+    </configuration>
+    <configuration default="true" type="KotlinStandaloneScriptRunConfigurationType">
+      <module name="mazhongjunsale" />
+      <option name="filePath" />
+      <method v="2">
+        <option name="Make" enabled="true" />
+      </method>
+    </configuration>
+  </component>
+  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="默认任务">
+      <changelist id="d0601eaf-b5ad-495a-9532-b47a8f8263d8" name="更改" comment="" />
+      <created>1746846044847</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1746846044847</updated>
+      <workItem from="1746846046397" duration="2973000" />
+    </task>
+    <servers />
+  </component>
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="version" value="3" />
+  </component>
+</project>

+ 111 - 0
pom.xml

@@ -0,0 +1,111 @@
+<?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>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.7.0</version>
+    </parent>
+
+    <groupId>com.example</groupId>
+    <artifactId>order-service</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <mybatis-plus.version>3.5.2</mybatis-plus.version>
+        <jjwt.version>0.9.1</jjwt.version>
+    </properties>
+
+    <dependencies>
+        <!-- Spring Boot -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+
+        <!-- MyBatis Plus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>${mybatis-plus.version}</version>
+        </dependency>
+
+        <!-- MySQL -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <!-- JWT -->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>${jjwt.version}</version>
+        </dependency>
+
+        <!-- Lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <!-- Test -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.validation</groupId>
+            <artifactId>jakarta.validation-api</artifactId>
+            <version>2.0.2</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 11 - 0
src/main/java/conm/zhentao/Application.java

@@ -0,0 +1,11 @@
+package conm.zhentao;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}

+ 26 - 0
src/main/java/conm/zhentao/config/SecurityConfig.java

@@ -0,0 +1,26 @@
+package conm.zhentao.config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                .csrf().disable()
+                .authorizeRequests()
+                .anyRequest().permitAll();
+    }
+}

+ 25 - 0
src/main/java/conm/zhentao/controller/AuthController.java

@@ -0,0 +1,25 @@
+package conm.zhentao.controller;
+
+
+import conm.zhentao.dto.LoginRequest;
+import conm.zhentao.dto.LoginResponse;
+import conm.zhentao.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api/auth")
+public class AuthController {
+
+    @Autowired
+    private UserService userService;
+
+    @PostMapping("/login")
+    public LoginResponse login(@Validated @RequestBody LoginRequest request) {
+        return userService.login(request);
+    }
+}

+ 45 - 0
src/main/java/conm/zhentao/controller/OrderController.java

@@ -0,0 +1,45 @@
+package conm.zhentao.controller;
+
+import conm.zhentao.dto.OrderCreateDTO;
+import conm.zhentao.entity.Order_info;
+import conm.zhentao.service.OrderService;
+import conm.zhentao.util.JwtUtil;
+import conm.zhentao.util.RedisUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/orders")
+public class OrderController {
+
+    @Autowired
+    private OrderService orderService;
+
+    @Autowired
+    private JwtUtil jwtUtil;
+
+    @Autowired
+    private RedisUtil redisUtil;
+
+    @PostMapping
+    public Order_info createOrder(
+            @Validated @RequestBody OrderCreateDTO orderCreateDTO,
+            @RequestHeader(value = "Authorization", required = true) String token) {
+        // 从token中获取用户ID
+        Long userId = getUserIdFromToken(token);
+        return orderService.createOrder(orderCreateDTO, userId);
+    }
+
+    private Long getUserIdFromToken(String token) {
+        // 如果使用JWT
+        if (token.startsWith("Bearer ")) {
+            token = token.substring(7);
+            // 解析JWT token获取用户ID
+            return jwtUtil.getUserIdFromToken(token);
+        } else {
+            // 从Redis中获取用户ID
+            return redisUtil.getUserIdFromToken(token);
+        }
+    }
+}

+ 13 - 0
src/main/java/conm/zhentao/dto/LoginRequest.java

@@ -0,0 +1,13 @@
+package conm.zhentao.dto;
+
+import lombok.Data;
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class LoginRequest {
+    @NotBlank(message = "用户名不能为空")
+    private String username;
+    
+    @NotBlank(message = "密码不能为空")
+    private String password;
+} 

+ 16 - 0
src/main/java/conm/zhentao/dto/LoginResponse.java

@@ -0,0 +1,16 @@
+package conm.zhentao.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class LoginResponse {
+    private String token;
+    private Long userId;
+    private String username;
+} 

+ 17 - 0
src/main/java/conm/zhentao/dto/OrderCreateDTO.java

@@ -0,0 +1,17 @@
+package conm.zhentao.dto;
+
+import lombok.Data;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@Data
+public class OrderCreateDTO {
+    @NotEmpty(message = "订单商品不能为空")
+    @Valid
+    private List<OrderItemDTO> items;
+    
+    private Long couponId;
+}
+

+ 15 - 0
src/main/java/conm/zhentao/dto/OrderItemDTO.java

@@ -0,0 +1,15 @@
+package conm.zhentao.dto;
+
+import lombok.Data;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+@Data
+public class OrderItemDTO {
+    @NotNull(message = "商品ID不能为空")
+    private Long skuId;
+    
+    @NotNull(message = "商品数量不能为空")
+    @Min(value = 1, message = "商品数量必须大于0")
+    private Integer quantity;
+}

+ 24 - 0
src/main/java/conm/zhentao/entity/OrderDetail.java

@@ -0,0 +1,24 @@
+package conm.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_order_detail")
+public class OrderDetail {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    
+    private Long orderId;
+    private Long skuId;
+    private String skuName;
+    private BigDecimal price;
+    private Integer quantity;
+    private BigDecimal totalAmount;
+    private LocalDateTime createTime;
+    private LocalDateTime updateTime;
+} 

+ 25 - 0
src/main/java/conm/zhentao/entity/Order_info.java

@@ -0,0 +1,25 @@
+package conm.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_order")
+public class Order_info {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long userId;
+    private String orderNo;
+    private BigDecimal totalAmount;
+    private BigDecimal discountAmount;
+    private BigDecimal actualAmount;
+    private Long couponId;
+    private Integer status;
+    private LocalDateTime createTime;
+    private LocalDateTime updateTime;
+}

+ 17 - 0
src/main/java/conm/zhentao/entity/Sku.java

@@ -0,0 +1,17 @@
+package conm.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.math.BigDecimal;
+
+@Data
+@TableName("t_sku")
+public class Sku {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String name;
+    private BigDecimal price;
+    private Integer stock;
+} 

+ 21 - 0
src/main/java/conm/zhentao/entity/User.java

@@ -0,0 +1,21 @@
+package conm.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_user")
+public class User {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String username;
+    private String password;
+    private String phone;
+    private String email;
+    private Integer status;
+    private LocalDateTime createTime;
+    private LocalDateTime updateTime;
+} 

+ 18 - 0
src/main/java/conm/zhentao/exception/BusinessException.java

@@ -0,0 +1,18 @@
+package conm.zhentao.exception;
+
+import lombok.Getter;
+
+@Getter
+public class BusinessException extends RuntimeException {
+    private final int code;
+
+    public BusinessException(String message) {
+        super(message);
+        this.code = 500;
+    }
+
+    public BusinessException(int code, String message) {
+        super(message);
+        this.code = code;
+    }
+} 

+ 57 - 0
src/main/java/conm/zhentao/exception/GlobalExceptionHandler.java

@@ -0,0 +1,57 @@
+package conm.zhentao.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.bind.MissingRequestHeaderException;
+import org.springframework.validation.BindException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import lombok.Data;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    @Data
+    public static class ErrorResponse {
+        private int code;
+        private String message;
+        
+        public ErrorResponse(int code, String message) {
+            this.code = code;
+            this.message = message;
+        }
+    }
+
+    @ExceptionHandler(BusinessException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public ErrorResponse handleBusinessException(BusinessException e) {
+        return new ErrorResponse(e.getCode(), e.getMessage());
+    }
+
+    @ExceptionHandler(MissingRequestHeaderException.class)
+    @ResponseStatus(HttpStatus.UNAUTHORIZED)
+    public ErrorResponse handleMissingRequestHeaderException(MissingRequestHeaderException e) {
+        return new ErrorResponse(401, "缺少认证信息,请先登录");
+    }
+
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public ErrorResponse handleValidationException(MethodArgumentNotValidException e) {
+        String message = e.getBindingResult().getFieldError().getDefaultMessage();
+        return new ErrorResponse(400, message);
+    }
+
+    @ExceptionHandler(BindException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public ErrorResponse handleBindException(BindException e) {
+        String message = e.getBindingResult().getFieldError().getDefaultMessage();
+        return new ErrorResponse(400, message);
+    }
+
+    @ExceptionHandler(RuntimeException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public ErrorResponse handleRuntimeException(RuntimeException e) {
+        return new ErrorResponse(500, e.getMessage());
+    }
+} 

+ 9 - 0
src/main/java/conm/zhentao/mapper/OrderDetailMapper.java

@@ -0,0 +1,9 @@
+package conm.zhentao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import conm.zhentao.entity.OrderDetail;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface OrderDetailMapper extends BaseMapper<OrderDetail> {
+} 

+ 9 - 0
src/main/java/conm/zhentao/mapper/OrderMapper.java

@@ -0,0 +1,9 @@
+package conm.zhentao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import conm.zhentao.entity.Order_info;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface OrderMapper extends BaseMapper<Order_info> {
+} 

+ 9 - 0
src/main/java/conm/zhentao/mapper/SkuMapper.java

@@ -0,0 +1,9 @@
+package conm.zhentao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import conm.zhentao.entity.Sku;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SkuMapper extends BaseMapper<Sku> {
+} 

+ 9 - 0
src/main/java/conm/zhentao/mapper/UserMapper.java

@@ -0,0 +1,9 @@
+package conm.zhentao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import conm.zhentao.entity.User;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface UserMapper extends BaseMapper<User> {
+} 

+ 8 - 0
src/main/java/conm/zhentao/service/CouponService.java

@@ -0,0 +1,8 @@
+package conm.zhentao.service;
+
+import java.math.BigDecimal;
+
+public interface CouponService {
+    BigDecimal calculateDiscount(Long couponId, BigDecimal totalAmount);
+    void useCoupon(Long couponId, Long userId);
+} 

+ 9 - 0
src/main/java/conm/zhentao/service/OrderService.java

@@ -0,0 +1,9 @@
+package conm.zhentao.service;
+
+
+import conm.zhentao.dto.OrderCreateDTO;
+import conm.zhentao.entity.Order_info;
+
+public interface OrderService {
+    Order_info createOrder(OrderCreateDTO orderCreateDTO, Long userId);
+} 

+ 8 - 0
src/main/java/conm/zhentao/service/SkuService.java

@@ -0,0 +1,8 @@
+package conm.zhentao.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import conm.zhentao.entity.Sku;
+
+public interface SkuService extends IService<Sku> {
+} 

+ 11 - 0
src/main/java/conm/zhentao/service/UserService.java

@@ -0,0 +1,11 @@
+package conm.zhentao.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import conm.zhentao.dto.LoginRequest;
+import conm.zhentao.dto.LoginResponse;
+import conm.zhentao.entity.User;
+
+
+public interface UserService extends IService<User> {
+    LoginResponse login(LoginRequest request);
+} 

+ 26 - 0
src/main/java/conm/zhentao/service/impl/CouponServiceImpl.java

@@ -0,0 +1,26 @@
+package conm.zhentao.service.impl;
+
+import conm.zhentao.service.CouponService;
+import org.springframework.stereotype.Service;
+import java.math.BigDecimal;
+
+@Service
+public class CouponServiceImpl implements CouponService {
+
+    @Override
+    public BigDecimal calculateDiscount(Long couponId, BigDecimal totalAmount) {
+        // 这里实现优惠券折扣计算逻辑
+        // 示例:假设优惠券是满100减10
+        if (totalAmount.compareTo(new BigDecimal("100")) >= 0) {
+            return new BigDecimal("10");
+        }
+        return BigDecimal.ZERO;
+    }
+
+    @Override
+    public void useCoupon(Long couponId, Long userId) {
+        // 这里实现优惠券使用逻辑
+        // 示例:更新优惠券状态为已使用
+        // couponMapper.updateStatus(couponId, userId, 1);
+    }
+} 

+ 122 - 0
src/main/java/conm/zhentao/service/impl/OrderServiceImpl.java

@@ -0,0 +1,122 @@
+package conm.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import conm.zhentao.dto.OrderCreateDTO;
+import conm.zhentao.dto.OrderItemDTO;
+import conm.zhentao.entity.OrderDetail;
+import conm.zhentao.entity.Order_info;
+import conm.zhentao.entity.Sku;
+import conm.zhentao.exception.BusinessException;
+import conm.zhentao.mapper.OrderDetailMapper;
+import conm.zhentao.mapper.OrderMapper;
+import conm.zhentao.service.CouponService;
+import conm.zhentao.service.OrderService;
+import conm.zhentao.service.SkuService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Service
+public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order_info> implements OrderService {
+
+    @Autowired
+    private SkuService skuService;
+
+    @Autowired
+    private CouponService couponService;
+
+    @Autowired
+    private OrderDetailMapper orderDetailMapper;
+
+    @Override
+    @Transactional
+    public Order_info createOrder(OrderCreateDTO orderCreateDTO, Long userId) {
+        // 1. 获取商品信息
+        List<Long> skuIds = orderCreateDTO.getItems().stream()
+                .map(OrderItemDTO::getSkuId)
+                .collect(Collectors.toList());
+        Map<Long, Sku> skuMap = skuService.listByIds(skuIds).stream()
+                .collect(Collectors.toMap(Sku::getId, sku -> sku));
+
+        // 2. 创建订单
+        Order_info order = new Order_info();
+        order.setUserId(userId);
+        order.setOrderNo(generateOrderNo());
+        order.setStatus(1);
+        order.setCreateTime(LocalDateTime.now());
+        order.setUpdateTime(LocalDateTime.now());
+
+        // 3. 创建订单明细并计算总金额
+        List<OrderDetail> orderDetails = new ArrayList<>();
+        BigDecimal totalAmount = BigDecimal.ZERO;
+
+        for (OrderItemDTO item : orderCreateDTO.getItems()) {
+            Sku sku = skuMap.get(item.getSkuId());
+            if (sku == null) {
+                throw new BusinessException(400, "商品不存在: " + item.getSkuId());
+            }
+
+            if (sku.getStock() < item.getQuantity()) {
+                throw new BusinessException(400, "商品库存不足: " + sku.getName());
+            }
+
+            OrderDetail detail = new OrderDetail();
+            detail.setSkuId(sku.getId());
+            detail.setSkuName(sku.getName());
+            detail.setPrice(sku.getPrice());
+            detail.setQuantity(item.getQuantity());
+            detail.setTotalAmount(sku.getPrice().multiply(new BigDecimal(item.getQuantity())));
+            detail.setCreateTime(LocalDateTime.now());
+            detail.setUpdateTime(LocalDateTime.now());
+            
+            orderDetails.add(detail);
+            totalAmount = totalAmount.add(detail.getTotalAmount());
+        }
+
+        // 4. 计算优惠金额
+        BigDecimal discountAmount = BigDecimal.ZERO;
+        if (orderCreateDTO.getCouponId() != null) {
+            try {
+                discountAmount = couponService.calculateDiscount(orderCreateDTO.getCouponId(), totalAmount);
+                order.setCouponId(orderCreateDTO.getCouponId());
+            } catch (Exception e) {
+                throw new BusinessException(400, "优惠券使用失败: " + e.getMessage());
+            }
+        }
+
+        // 5. 设置订单金额
+        order.setTotalAmount(totalAmount);
+        order.setDiscountAmount(discountAmount);
+        order.setActualAmount(totalAmount.subtract(discountAmount));
+
+        // 6. 保存订单和订单明细
+        save(order);
+        for (OrderDetail detail : orderDetails) {
+            detail.setOrderId(order.getId());
+            orderDetailMapper.insert(detail);
+        }
+
+        // 7. 使用优惠券
+        if (orderCreateDTO.getCouponId() != null) {
+            try {
+                couponService.useCoupon(orderCreateDTO.getCouponId(), userId);
+            } catch (Exception e) {
+                throw new BusinessException(400, "优惠券使用失败: " + e.getMessage());
+            }
+        }
+
+        return order;
+    }
+
+    private String generateOrderNo() {
+        // 生成订单号的逻辑,可以使用时间戳+随机数
+        return "ORDER" + System.currentTimeMillis() + (int)(Math.random() * 1000);
+    }
+} 

+ 12 - 0
src/main/java/conm/zhentao/service/impl/SkuServiceImpl.java

@@ -0,0 +1,12 @@
+package conm.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+
+import conm.zhentao.entity.Sku;
+import conm.zhentao.mapper.SkuMapper;
+import conm.zhentao.service.SkuService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements SkuService {
+} 

+ 55 - 0
src/main/java/conm/zhentao/service/impl/UserServiceImpl.java

@@ -0,0 +1,55 @@
+package conm.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import conm.zhentao.dto.LoginRequest;
+import conm.zhentao.dto.LoginResponse;
+import conm.zhentao.entity.User;
+import conm.zhentao.exception.BusinessException;
+import conm.zhentao.mapper.UserMapper;
+import conm.zhentao.service.UserService;
+import conm.zhentao.util.TokenUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.stereotype.Service;
+
+@Service
+public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
+
+    @Autowired
+    private TokenUtil tokenUtil;
+
+    private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+
+    @Override
+    public LoginResponse login(LoginRequest request) {
+        // 1. 查询用户
+        User user = getOne(new LambdaQueryWrapper<User>()
+                .eq(User::getUsername, request.getUsername()));
+
+        if (user == null) {
+            throw new BusinessException(400, "用户名或密码错误");
+        }
+
+        // 2. 验证密码
+        if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
+            throw new BusinessException(400, "用户名或密码错误");
+        }
+
+        // 3. 检查用户状态
+        if (user.getStatus() != 1) {
+            throw new BusinessException(400, "账号已被禁用");
+        }
+
+        // 4. 生成token
+        String token = tokenUtil.generateToken(user.getId());
+
+        // 5. 返回登录信息
+        return LoginResponse.builder()
+                .token(token)
+                .userId(user.getId())
+                .username(user.getUsername())
+                .build();
+    }
+
+}

+ 21 - 0
src/main/java/conm/zhentao/util/JwtUtil.java

@@ -0,0 +1,21 @@
+package conm.zhentao.util;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class JwtUtil {
+    
+    @Value("${jwt.secret}")
+    private String secret;
+
+    public Long getUserIdFromToken(String token) {
+        Claims claims = Jwts.parser()
+                .setSigningKey(secret)
+                .parseClaimsJws(token)
+                .getBody();
+        return Long.parseLong(claims.getSubject());
+    }
+} 

+ 20 - 0
src/main/java/conm/zhentao/util/RedisUtil.java

@@ -0,0 +1,20 @@
+package conm.zhentao.util;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class RedisUtil {
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    public long getUserIdFromToken(String token) {
+        String userId = redisTemplate.opsForValue().get("token:" + token);
+        if (userId == null) {
+            throw new RuntimeException("Token无效或已过期");
+        }
+        return Long.parseLong(userId);
+    }
+} 

+ 72 - 0
src/main/java/conm/zhentao/util/TokenUtil.java

@@ -0,0 +1,72 @@
+package conm.zhentao.util;
+
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class TokenUtil {
+
+    @Value("${jwt.secret}")
+    private String jwtSecret;
+
+    @Value("${jwt.expiration}")
+    private Long jwtExpiration;
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    /**
+     * 生成JWT token
+     */
+    public String generateJwtToken(Long userId) {
+        Date now = new Date();
+        Date expiryDate = new Date(now.getTime() + jwtExpiration);
+
+        return Jwts.builder()
+                .setSubject(String.valueOf(userId))
+                .setIssuedAt(now)
+                .setExpiration(expiryDate)
+                .signWith(SignatureAlgorithm.HS512, jwtSecret)
+                .compact();
+    }
+
+    /**
+     * 生成Redis token
+     */
+    public String generateRedisToken(Long userId) {
+        String token = UUID.randomUUID().toString().replace("-", "");
+        // 存储到Redis,设置过期时间
+        redisTemplate.opsForValue().set(
+            "token:" + token,
+            String.valueOf(userId),
+            jwtExpiration,
+            TimeUnit.MILLISECONDS
+        );
+        return token;
+    }
+
+    /**
+     * 生成token(默认使用JWT)
+     */
+    public String generateToken(Long userId) {
+        return "Bearer " + generateJwtToken(userId);
+    }
+
+    /**
+     * 生成token(指定类型)
+     */
+    public String generateToken(Long userId, boolean useRedis) {
+        if (useRedis) {
+            return generateRedisToken(userId);
+        }
+        return "Bearer " + generateJwtToken(userId);
+    }
+} 

+ 27 - 0
src/main/resources/application.yml

@@ -0,0 +1,27 @@
+server:
+  port: 8080
+
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf-8&useSSL=false
+    username: root
+    password: root
+
+  redis:
+    host: localhost
+    port: 6379
+    database: 0
+
+mybatis-plus:
+  mapper-locations: classpath:mapper/*.xml
+  type-aliases-package: com.example.entity
+  configuration:
+    map-underscore-to-camel-case: true
+  global-config:
+    db-config:
+      id-type: auto
+
+jwt:
+  secret: your-secret-key
+  expiration: 86400000 # 24 hours