diff --git a/pom.xml b/pom.xml
index e63083c..e5900c9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,6 +33,7 @@
1.18.30
3.5.5
4.3.1
+ 8.0.33
@@ -49,7 +50,7 @@
com.mysql
mysql-connector-j
- runtime
+ ${mysql.version}
com.baomidou
@@ -69,6 +70,21 @@
true
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+
org.springdoc
diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/Constant/DeletedConstant.java b/src/main/java/icu/sunway/ai_spring_example/Common/Constant/DeletedConstant.java
new file mode 100644
index 0000000..9ee3ec3
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Common/Constant/DeletedConstant.java
@@ -0,0 +1,6 @@
+package icu.sunway.ai_spring_example.Common.Constant;
+
+public class DeletedConstant {
+ public static final Integer DELETED = 1;
+ public static final Integer NOT_DELETED = 0;
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/Constant/JwtClaimsConstant.java b/src/main/java/icu/sunway/ai_spring_example/Common/Constant/JwtClaimsConstant.java
new file mode 100644
index 0000000..96b0d76
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Common/Constant/JwtClaimsConstant.java
@@ -0,0 +1,10 @@
+package icu.sunway.ai_spring_example.Common.Constant;
+
+public class JwtClaimsConstant {
+
+ public static final String USER_ID = "userId";
+ public static final String PHONE = "phone";
+ public static final String USERNAME = "username";
+ public static final String NAME = "name";
+
+}
\ No newline at end of file
diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/Constant/MessageConstant.java b/src/main/java/icu/sunway/ai_spring_example/Common/Constant/MessageConstant.java
new file mode 100644
index 0000000..db61dbe
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Common/Constant/MessageConstant.java
@@ -0,0 +1,21 @@
+package icu.sunway.ai_spring_example.Common.Constant;
+
+/**
+ * 信息提示常量类
+ */
+public class MessageConstant {
+ public static final String ALREADY_EXISTS = "账号已存在";
+ public static final String PASSWORD_ERROR = "密码错误";
+ public static final String ACCOUNT_NOT_FOUND = "账号不存在";
+ public static final String ACCOUNT_LOCKED = "账号被锁定";
+ public static final String UNKNOWN_ERROR = "未知错误";
+ public static final String USER_NOT_LOGIN = "用户未登录";
+ public static final String SHOPPING_CART_IS_NULL = "购物车数据为空,不能下单";
+ public static final String ADDRESS_BOOK_IS_NULL = "用户地址为空,不能下单";
+ public static final String LOGIN_FAILED = "登录失败";
+ public static final String UPLOAD_FAILED = "文件上传失败";
+ public static final String PASSWORD_EDIT_FAILED = "密码修改失败";
+ public static final String ORDER_STATUS_ERROR = "订单状态错误";
+ public static final String ORDER_NOT_FOUND = "订单不存在";
+ public static final String PRODUCT_NOT_FOUND = "商品不存在";
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/Constant/StatusConstant.java b/src/main/java/icu/sunway/ai_spring_example/Common/Constant/StatusConstant.java
new file mode 100644
index 0000000..9fd11ef
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Common/Constant/StatusConstant.java
@@ -0,0 +1,14 @@
+package icu.sunway.ai_spring_example.Common.Constant;
+
+/**
+ * 状态常量,启用或者禁用
+ */
+public class StatusConstant {
+
+ //启用
+ public static final Integer ENABLE = 1;
+
+ //禁用
+ public static final Integer DISABLE = 0;
+}
+
diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/Exception/AccountNotFoundException.java b/src/main/java/icu/sunway/ai_spring_example/Common/Exception/AccountNotFoundException.java
new file mode 100644
index 0000000..55d97f1
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Common/Exception/AccountNotFoundException.java
@@ -0,0 +1,15 @@
+package icu.sunway.ai_spring_example.Common.Exception;
+
+/**
+ * 账号不存在异常
+ */
+public class AccountNotFoundException extends BaseException {
+
+ public AccountNotFoundException() {
+ }
+
+ public AccountNotFoundException(String msg) {
+ super(msg);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/Exception/BaseException.java b/src/main/java/icu/sunway/ai_spring_example/Common/Exception/BaseException.java
new file mode 100644
index 0000000..3abe031
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Common/Exception/BaseException.java
@@ -0,0 +1,15 @@
+package icu.sunway.ai_spring_example.Common.Exception;
+
+/**
+ * 业务异常
+ */
+public class BaseException extends RuntimeException {
+
+ public BaseException() {
+ }
+
+ public BaseException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/Exception/BusinessException.java b/src/main/java/icu/sunway/ai_spring_example/Common/Exception/BusinessException.java
new file mode 100644
index 0000000..afeaeb7
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Common/Exception/BusinessException.java
@@ -0,0 +1,11 @@
+package icu.sunway.ai_spring_example.Common.Exception;
+/**
+ * 交易异常
+ */
+public class BusinessException extends BaseException {
+ public BusinessException() {
+ }
+ public BusinessException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/Properties/JwtProperties.java b/src/main/java/icu/sunway/ai_spring_example/Common/Properties/JwtProperties.java
new file mode 100644
index 0000000..59daf1d
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Common/Properties/JwtProperties.java
@@ -0,0 +1,18 @@
+package icu.sunway.ai_spring_example.Common.Properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "zhiwei.jwt")
+@Data
+public class JwtProperties {
+
+ /**
+ * 用户生成jwt令牌相关配置
+ */
+ private String userSecretKey;
+ private long userTtl;
+ private String userTokenName;
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/ResponseEntity.java b/src/main/java/icu/sunway/ai_spring_example/Common/Response/ResponseEntity.java
similarity index 98%
rename from src/main/java/icu/sunway/ai_spring_example/Common/ResponseEntity.java
rename to src/main/java/icu/sunway/ai_spring_example/Common/Response/ResponseEntity.java
index eb33a3d..86e39aa 100644
--- a/src/main/java/icu/sunway/ai_spring_example/Common/ResponseEntity.java
+++ b/src/main/java/icu/sunway/ai_spring_example/Common/Response/ResponseEntity.java
@@ -1,4 +1,4 @@
-package icu.sunway.ai_spring_example.Common;
+package icu.sunway.ai_spring_example.Common.Response;
import lombok.AllArgsConstructor;
import lombok.Data;
diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/Utils/JwtUtil.java b/src/main/java/icu/sunway/ai_spring_example/Common/Utils/JwtUtil.java
new file mode 100644
index 0000000..b837b95
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Common/Utils/JwtUtil.java
@@ -0,0 +1,69 @@
+package icu.sunway.ai_spring_example.Common.Utils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+
+import javax.crypto.SecretKey;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.Map;
+
+public class JwtUtil {
+ /**
+ * 生成jwt
+ * 使用Hs256算法, 私匙使用固定秘钥
+ *
+ * @param secretKey jwt秘钥
+ * @param ttlMillis jwt过期时间(毫秒)
+ * @param claims 设置的信息
+ * @return
+ */
+ public static String createJWT(String secretKey, long ttlMillis, Map claims) {
+ // 生成安全密钥
+ SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
+
+ // 指定签名的时候使用的签名算法,也就是header那部分
+ SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
+
+ // 生成JWT的时间
+ long expMillis = System.currentTimeMillis() + ttlMillis;
+ Date exp = new Date(expMillis);
+
+ // 设置jwt的body
+ JwtBuilder builder = Jwts.builder()
+ // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
+ .setClaims(claims)
+ // 设置签名使用的签名算法和签名使用的秘钥
+ .signWith(key, signatureAlgorithm)
+ // 设置过期时间
+ .setExpiration(exp);
+
+ return builder.compact();
+ }
+
+ /**
+ * Token解密
+ *
+ * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
+ * @param token 加密后的token
+ * @return
+ */
+ public static Claims parseJWT(String secretKey, String token) {
+ // 生成安全密钥
+ SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
+
+ // 得到DefaultJwtParser
+ Claims claims = Jwts.parserBuilder()
+ // 设置签名的秘钥
+ .setSigningKey(key)
+ // 设置需要解析的jwt
+ .build()
+ .parseClaimsJws(token)
+ .getBody();
+ return claims;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/icu/sunway/ai_spring_example/Config/MyBatisPlusConfig.java b/src/main/java/icu/sunway/ai_spring_example/Config/MyBatisPlusConfig.java
new file mode 100644
index 0000000..86a937d
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Config/MyBatisPlusConfig.java
@@ -0,0 +1,22 @@
+package icu.sunway.ai_spring_example.Config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * MyBatis-Plus核心配置(分页插件等)
+ */
+@Configuration
+public class MyBatisPlusConfig {
+
+ @Bean
+ public MybatisPlusInterceptor mybatisPlusInterceptor() {
+ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+ // 分页插件
+ interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
+ return interceptor;
+ }
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Config/MyMetaObjectHandler.java b/src/main/java/icu/sunway/ai_spring_example/Config/MyMetaObjectHandler.java
new file mode 100644
index 0000000..dea8310
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Config/MyMetaObjectHandler.java
@@ -0,0 +1,26 @@
+package icu.sunway.ai_spring_example.Config;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.context.annotation.Configuration;
+import java.time.LocalDateTime;
+
+/**
+ * MyBatis-Plus字段自动填充配置
+ */
+@Configuration
+public class MyMetaObjectHandler implements MetaObjectHandler {
+
+ @Override
+ public void insertFill(MetaObject metaObject) {
+ // 插入时填充创建时间和更新时间
+ this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
+ this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+ }
+
+ @Override
+ public void updateFill(MetaObject metaObject) {
+ // 更新时填充更新时间
+ this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+ }
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Controllers/Admin.java b/src/main/java/icu/sunway/ai_spring_example/Controllers/Admin.java
index 97fbb98..057b0da 100644
--- a/src/main/java/icu/sunway/ai_spring_example/Controllers/Admin.java
+++ b/src/main/java/icu/sunway/ai_spring_example/Controllers/Admin.java
@@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
-import icu.sunway.ai_spring_example.Common.ResponseEntity;
+import icu.sunway.ai_spring_example.Common.Response.ResponseEntity;
/**
* 用户管理后台接口
diff --git a/src/main/java/icu/sunway/ai_spring_example/Controllers/Auth.java b/src/main/java/icu/sunway/ai_spring_example/Controllers/Auth.java
index e975a8e..62826a4 100644
--- a/src/main/java/icu/sunway/ai_spring_example/Controllers/Auth.java
+++ b/src/main/java/icu/sunway/ai_spring_example/Controllers/Auth.java
@@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
-import icu.sunway.ai_spring_example.Common.ResponseEntity;
+import icu.sunway.ai_spring_example.Common.Response.ResponseEntity;
/**
* 认证控制器
diff --git a/src/main/java/icu/sunway/ai_spring_example/Controllers/Course.java b/src/main/java/icu/sunway/ai_spring_example/Controllers/Course.java
index d4bcda5..dfbf458 100644
--- a/src/main/java/icu/sunway/ai_spring_example/Controllers/Course.java
+++ b/src/main/java/icu/sunway/ai_spring_example/Controllers/Course.java
@@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
-import icu.sunway.ai_spring_example.Common.ResponseEntity;
+import icu.sunway.ai_spring_example.Common.Response.ResponseEntity;
/**
* 课程控制器
diff --git a/src/main/java/icu/sunway/ai_spring_example/Controllers/ProductController.java b/src/main/java/icu/sunway/ai_spring_example/Controllers/ProductController.java
new file mode 100644
index 0000000..318c1e0
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Controllers/ProductController.java
@@ -0,0 +1,86 @@
+package icu.sunway.ai_spring_example.Controllers;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import icu.sunway.ai_spring_example.Common.Response.ResponseEntity;
+import icu.sunway.ai_spring_example.Service.ProductService;
+import icu.sunway.ai_spring_example.pojo.Dto.ProductDTO;
+import icu.sunway.ai_spring_example.pojo.Vo.ProductListVO;
+import icu.sunway.ai_spring_example.pojo.Vo.ProductVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/product")
+@Slf4j
+public class ProductController {
+
+ @Autowired
+ private ProductService productService;
+
+ /**
+ * 新增商品
+ */
+ @PostMapping
+ public ResponseEntity save(@RequestBody ProductDTO productDTO) {
+ log.info("新增商品: {}", productDTO);
+ productService.saveProduct(productDTO);
+ return ResponseEntity.success("商品新增成功");
+ }
+
+ /**
+ * 修改商品
+ */
+ @PutMapping
+ public ResponseEntity update(@RequestBody ProductDTO productDTO) {
+ log.info("修改商品: {}", productDTO);
+ productService.updateProduct(productDTO);
+ return ResponseEntity.success("商品修改成功");
+ }
+
+ /**
+ * 商品上下架
+ */
+ @PutMapping("/status/{id}/{status}")
+ public ResponseEntity updateStatus(@PathVariable Long id, @PathVariable Integer status) {
+ log.info("商品上下架: id={}, status={}", id, status);
+ productService.updateStatus(id, status);
+ return ResponseEntity.success("状态更新成功");
+ }
+
+ /**
+ * 分页查询商品列表
+ */
+ @GetMapping("/page")
+ public ResponseEntity