Przeglądaj źródła

微信小程序点餐模块

panzhilin 4 miesięcy temu
rodzic
commit
1609f808e6
34 zmienionych plików z 1785 dodań i 0 usunięć
  1. 28 0
      alien-dining/README.md
  2. 15 0
      alien-dining/doc/gateway-route-example.yml
  3. 84 0
      alien-dining/doc/nacos-config-example.yml
  4. 82 0
      alien-dining/doc/nacos-config-ready.yml
  5. 354 0
      alien-dining/pom.xml
  6. 24 0
      alien-dining/src/main/java/shop/alien/dining/AlienDiningApplication.java
  7. 26 0
      alien-dining/src/main/java/shop/alien/dining/annotation/OperationLog.java
  8. 28 0
      alien-dining/src/main/java/shop/alien/dining/aspect/OperationLogAspect.java
  9. 261 0
      alien-dining/src/main/java/shop/alien/dining/config/BaseRedisService.java
  10. 34 0
      alien-dining/src/main/java/shop/alien/dining/config/CustomAccessDeniedHandler.java
  11. 59 0
      alien-dining/src/main/java/shop/alien/dining/config/DruidConfig.java
  12. 14 0
      alien-dining/src/main/java/shop/alien/dining/config/MyBatisPlusPageConfig.java
  13. 69 0
      alien-dining/src/main/java/shop/alien/dining/config/SecurityConfig.java
  14. 58 0
      alien-dining/src/main/java/shop/alien/dining/config/SwaggerConfig.java
  15. 19 0
      alien-dining/src/main/java/shop/alien/dining/config/WebConfig.java
  16. 41 0
      alien-dining/src/main/java/shop/alien/dining/controller/DiningMenuController.java
  17. 48 0
      alien-dining/src/main/java/shop/alien/dining/enums/OrderStatus.java
  18. 17 0
      alien-dining/src/main/java/shop/alien/dining/feign/DiningServiceFeign.java
  19. 67 0
      alien-dining/src/main/java/shop/alien/dining/filter/PreAuthFilter.java
  20. 26 0
      alien-dining/src/main/java/shop/alien/dining/listener/RedisKeyExpirationListener.java
  21. 13 0
      alien-dining/src/main/java/shop/alien/dining/service/DiningMenuService.java
  22. 21 0
      alien-dining/src/main/java/shop/alien/dining/service/impl/DiningMenuServiceImpl.java
  23. 31 0
      alien-dining/src/main/java/shop/alien/dining/strategy/payment/PaymentStrategy.java
  24. 34 0
      alien-dining/src/main/java/shop/alien/dining/strategy/payment/impl/WeChatPaymentStrategyImpl.java
  25. 16 0
      alien-dining/src/main/java/shop/alien/dining/util/WeChatPayUtil.java
  26. 26 0
      alien-dining/src/main/resources/bootstrap-bug.yml
  27. 26 0
      alien-dining/src/main/resources/bootstrap-dev.yml
  28. 26 0
      alien-dining/src/main/resources/bootstrap-prod.yml
  29. 26 0
      alien-dining/src/main/resources/bootstrap-test.yml
  30. 26 0
      alien-dining/src/main/resources/bootstrap-uat.yml
  31. 4 0
      alien-dining/src/main/resources/bootstrap.yml
  32. 178 0
      alien-dining/src/main/resources/logback-spring.xml
  33. 3 0
      alien-entity/src/main/java/shop/alien/entity/result/R.java
  34. 1 0
      pom.xml

+ 28 - 0
alien-dining/README.md

@@ -0,0 +1,28 @@
+# alien-dining
+
+微信点餐程序模块
+
+## 模块说明
+
+本模块用于实现微信点餐相关功能,包括:
+- 菜单管理
+- 订单管理
+- 支付集成
+- 微信小程序对接
+
+## 技术栈
+
+- Spring Boot 2.3.2
+- Spring Cloud Hoxton.SR1
+- MyBatis Plus 3.2.0
+- Nacos 配置中心/注册中心
+- MySQL 8.0
+- Redis
+- 微信支付 SDK
+
+## 启动说明
+
+1. 确保已配置好 Nacos 服务
+2. 配置数据库连接信息
+3. 运行 `AlienDiningApplication` 主类
+

+ 15 - 0
alien-dining/doc/gateway-route-example.yml

@@ -0,0 +1,15 @@
+# 网关路由配置示例
+# 将此配置添加到 Nacos 的 alien-gateway.yml 配置文件中
+
+spring:
+  cloud:
+    gateway:
+      routes:
+        # 微信点餐模块路由配置
+        - id: aliendining
+          uri: http://${route_or_local_ip}:30014
+          predicates:
+            - Path=/aliendining/**
+          filters:
+            - StripPrefix=1
+

+ 84 - 0
alien-dining/doc/nacos-config-example.yml

@@ -0,0 +1,84 @@
+server:
+  port: 8080
+  servlet:
+    context-path: /dining
+
+spring:
+  application:
+    name: alien-dining
+  
+  # 数据源配置
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    druid:
+      driver-class-name: com.mysql.cj.jdbc.Driver
+      url: jdbc:mysql://your-mysql-host:3306/alien_dining?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+      username: your-username
+      password: your-password
+      initial-size: 5
+      min-idle: 5
+      max-active: 20
+      max-wait: 60000
+      time-between-eviction-runs-millis: 60000
+      min-evictable-idle-time-millis: 300000
+      validation-query: SELECT 1
+      test-while-idle: true
+      test-on-borrow: false
+      test-on-return: false
+      pool-prepared-statements: true
+      max-pool-prepared-statement-per-connection-size: 20
+      filters: stat,wall,slf4j
+      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
+  
+  # Redis配置
+  redis:
+    host: your-redis-host
+    port: 6379
+    password: your-redis-password
+    database: 0
+    timeout: 3000
+    lettuce:
+      pool:
+        max-active: 8
+        max-idle: 8
+        min-idle: 0
+        max-wait: -1
+  
+  # MyBatis Plus配置
+  mybatis-plus:
+    mapper-locations: classpath*:mapper/**/*.xml
+    type-aliases-package: shop.alien.entity
+    configuration:
+      map-underscore-to-camel-case: true
+      log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
+    global-config:
+      db-config:
+        id-type: auto
+        logic-delete-field: deleted
+        logic-delete-value: 1
+        logic-not-delete-value: 0
+
+# 日志配置
+logging:
+  level:
+    root: info
+    shop.alien.dining: debug
+    shop.alien.mapper: debug
+  path: C:/project/ext/log
+
+# 微信支付配置
+wechat:
+  pay:
+    app-id: your-wechat-app-id
+    mch-id: your-merchant-id
+    api-key: your-api-key
+    cert-path: your-cert-path
+    notify-url: https://your-domain.com/dining/pay/notify
+
+# 点餐业务配置
+dining:
+  # 订单超时时间(分钟)
+  order-timeout: 30
+  # 自动取消未支付订单时间(分钟)
+  auto-cancel-time: 15
+

+ 82 - 0
alien-dining/doc/nacos-config-ready.yml

@@ -0,0 +1,82 @@
+server:
+  port: 30014
+  servlet:
+    context-path: /dining
+
+spring:
+  application:
+    name: alien-dining
+  
+  # 数据源配置
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    druid:
+      driver-class-name: com.mysql.cj.jdbc.Driver
+      url: jdbc:mysql://your-mysql-host:3306/alien_dining?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+      username: your-username
+      password: ENC(your-encrypted-password)
+      initial-size: 5
+      min-idle: 5
+      max-active: 20
+      max-wait: 60000
+      time-between-eviction-runs-millis: 60000
+      min-evictable-idle-time-millis: 300000
+      validation-query: SELECT 1
+      test-while-idle: true
+      test-on-borrow: false
+      test-on-return: false
+      pool-prepared-statements: true
+      max-pool-prepared-statement-per-connection-size: 20
+      filters: stat,wall,slf4j
+      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
+  
+  # Redis配置
+  redis:
+    host: your-redis-host
+    port: 6379
+    password: ENC(your-encrypted-redis-password)
+    database: 0
+    timeout: 3000
+    lettuce:
+      pool:
+        max-active: 8
+        max-idle: 8
+        min-idle: 0
+        max-wait: -1
+  
+  # MyBatis Plus配置
+  mybatis-plus:
+    mapper-locations: classpath*:mapper/**/*.xml
+    type-aliases-package: shop.alien.entity
+    configuration:
+      map-underscore-to-camel-case: true
+      log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
+    global-config:
+      db-config:
+        id-type: auto
+        logic-delete-field: deleted
+        logic-delete-value: 1
+        logic-not-delete-value: 0
+
+# 日志配置
+logging:
+  level:
+    root: info
+    shop.alien.dining: debug
+    shop.alien.mapper: debug
+  path: C:/project/ext/log
+
+# 微信支付配置
+wechat:
+  pay:
+    app-id: your-wechat-app-id
+    mch-id: your-merchant-id
+    api-key: ENC(your-encrypted-api-key)
+    cert-path: your-cert-path
+    notify-url: https://your-domain.com/dining/pay/notify
+
+# 点餐业务配置
+dining:
+  order-timeout: 30
+  auto-cancel-time: 15
+

+ 354 - 0
alien-dining/pom.xml

@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>shop.alien</groupId>
+        <artifactId>alien-cloud</artifactId>
+        <version>1.0.0</version>
+    </parent>
+
+    <artifactId>alien-dining</artifactId>
+    <version>1.0.0</version>
+    <name>alien-dining</name>
+    <description>微信点餐程序</description>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.fasterxml.jackson.core</groupId>
+                    <artifactId>jackson-databind</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <!--redis连接池需要此依赖-->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- mybatis-plus代码生成器 Start -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-generator</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>jsqlparser</artifactId>
+                    <groupId>com.github.jsqlparser</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>mybatis</artifactId>
+                    <groupId>org.mybatis</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>mybatis-spring</artifactId>
+                    <groupId>org.mybatis</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-annotation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-extension</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-core</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <!-- mybatisPlus Freemarker 模版引擎 -->
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+        </dependency>
+        <!-- mybatis-plus代码生成器 End -->
+
+        <!--Swagger Start-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>swagger-annotations</artifactId>
+                    <groupId>io.swagger</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>swagger-models</artifactId>
+                    <groupId>io.swagger</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>swagger-bootstrap-ui</artifactId>
+        </dependency>
+        <!--Swagger End-->
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <!--token-->
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alipay.sdk</groupId>
+            <artifactId>alipay-sdk-java</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dysmsapi20170525</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.netease.yidun</groupId>
+            <artifactId>yidun-java-sdk</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+        </dependency>
+
+        <!-- openfeign -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.github.openfeign</groupId>
+            <artifactId>feign-okhttp</artifactId>
+        </dependency>
+        <!-- openfeign -->
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-entity</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-config</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-util</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <!-- 微信支付 -->
+        <dependency>
+            <groupId>com.github.wechatpay-apiv3</groupId>
+            <artifactId>wechatpay-java</artifactId>
+            <version>0.2.17</version>
+        </dependency>
+
+        <!-- Google Gson -->
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.11.0</version>
+        </dependency>
+        <!-- OkHttp -->
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>4.12.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>jakarta.validation</groupId>
+            <artifactId>jakarta.validation-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <!-- 指定项目编译时的java版本和编码方式 -->
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <target>1.8</target>
+                    <source>1.8</source>
+                    <encoding>UTF-8</encoding>
+                    <parameters>true</parameters>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <!-- 是否要把第三方jar加入到类构建路径 -->
+                            <addClasspath>true</addClasspath>
+                            <!-- 外部依赖jar包的最终位置 -->
+                            <classpathPrefix>lib/</classpathPrefix>
+                            <!--指定jar程序入口-->
+                            <mainClass>shop.alien.dining.AlienDiningApplication</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+            <!--拷贝依赖到jar外面的lib目录-->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <!-- 拷贝项目依赖包到lib/目录下 -->
+                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
+                            <overWriteReleases>false</overWriteReleases>
+                            <overWriteSnapshots>false</overWriteSnapshots>
+                            <overWriteIfNewer>true</overWriteIfNewer>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
+

+ 24 - 0
alien-dining/src/main/java/shop/alien/dining/AlienDiningApplication.java

@@ -0,0 +1,24 @@
+package shop.alien.dining;
+
+import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@ComponentScan({"shop.alien.dining.*","shop.alien.util.*","shop.alien.config.http","shop.alien.config.properties"})
+@EnableSwaggerBootstrapUI
+@MapperScan({"shop.alien.mapper"})
+@SpringBootApplication
+@EnableFeignClients(basePackages = "shop.alien.dining.feign")
+@EnableScheduling
+public class   AlienDiningApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(AlienDiningApplication.class, args);
+    }
+
+}
+

+ 26 - 0
alien-dining/src/main/java/shop/alien/dining/annotation/OperationLog.java

@@ -0,0 +1,26 @@
+package shop.alien.dining.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 操作日志注解
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface OperationLog {
+    /**
+     * 操作描述
+     */
+    String value() default "";
+
+    /**
+     * 操作类型
+     */
+    String type() default "";
+}
+

+ 28 - 0
alien-dining/src/main/java/shop/alien/dining/aspect/OperationLogAspect.java

@@ -0,0 +1,28 @@
+package shop.alien.dining.aspect;
+
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.springframework.stereotype.Component;
+import shop.alien.dining.annotation.OperationLog;
+
+/**
+ * 操作日志切面
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+@Slf4j
+@Aspect
+@Component
+public class OperationLogAspect {
+
+    @Before("@annotation(operationLog)")
+    public void before(JoinPoint joinPoint, OperationLog operationLog) {
+        // TODO: 实现操作日志记录逻辑
+        log.info("操作日志: {}", operationLog.value());
+    }
+}
+

+ 261 - 0
alien-dining/src/main/java/shop/alien/dining/config/BaseRedisService.java

@@ -0,0 +1,261 @@
+package shop.alien.dining.config;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.geo.*;
+import org.springframework.data.redis.connection.RedisGeoCommands;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Redis基础服务类
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2022/3/14 15:14
+ */
+@Component
+@RequiredArgsConstructor
+public class BaseRedisService {
+
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 添加List值, 向右
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setListRight(String key, String value) {
+        stringRedisTemplate.opsForList().rightPush(key, value);
+    }
+
+    /**
+     * 取出并删除列表中的所有元素(原子操作)
+     */
+    public List<String> popBatchFromList(String key) {
+        String luaScript =
+                "local elements = redis.call('LRANGE', KEYS[1], 0, -1) " +
+                        "redis.call('DEL', KEYS[1]) " +
+                        "return elements ";
+
+        RedisScript<List> script = RedisScript.of(luaScript, List.class);
+        return stringRedisTemplate.execute(script, Collections.singletonList(key));
+    }
+
+    /**
+     * 判断是否存在指定key
+     * @param key
+     * @return
+     */
+    public boolean hasKey(String key) {
+        Boolean exists = stringRedisTemplate.hasKey(key);
+        return exists != null && exists;
+    }
+
+    /**
+     * 添加List, 所有
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setList(String key, List<String> value) {
+        stringRedisTemplate.opsForList().rightPushAll(key, value);
+    }
+
+    /**
+     * 从List左边取值并删除
+     *
+     * @param key 键
+     * @return 删除的值
+     */
+    public String getListLeft(String key) {
+        return stringRedisTemplate.opsForList().leftPop(key);
+    }
+
+    /**
+     * 获取List所有
+     *
+     * @param key 键
+     * @return List<String>
+     */
+    public List<String> getList(String key) {
+        return stringRedisTemplate.opsForList().range(key, 0, -1);
+    }
+
+    /**
+     * 添加Set
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setSetList(String key, String value) {
+        stringRedisTemplate.opsForSet().add(key, value);
+    }
+
+    /**
+     * 获取Set
+     *
+     * @param key 键
+     * @return Set<String>
+     */
+    public Set<String> getSetList(String key) {
+        return stringRedisTemplate.opsForSet().members(key);
+    }
+
+    /**
+     * 添加String值, 不设置过期时间
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setString(String key, String value) {
+        set(key, value, null);
+    }
+
+    /**
+     * 添加String值, 并设置过期时间
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     */
+    public void setString(String key, String value, Long timeOut) {
+        set(key, value, timeOut);
+    }
+
+    /**
+     * 设置超时时间
+     *
+     * @param key     键
+     * @param timeOut 时长(秒)
+     */
+    public void setTimeOut(String key, Long timeOut) {
+        stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 传入object对象
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     */
+    private void set(String key, Object value, Long timeOut) {
+        if (value != null) {
+            if (value instanceof String) {
+                String setValue = (String) value;
+                stringRedisTemplate.opsForValue().set(key, setValue);
+            }
+            //设置有效期
+            if (timeOut != null) {
+                stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
+            }
+        }
+    }
+
+    /**
+     * 使用key查找redis信息
+     *
+     * @param key 键
+     * @return
+     */
+    public String getString(String key) {
+        return stringRedisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 使用key删除redis信息
+     *
+     * @param key 键
+     */
+    public void delete(String key) {
+        stringRedisTemplate.delete(key);
+    }
+
+    /**
+     * 获取分布式锁
+     * @param lockKey 锁的标识
+     * @return 锁的value值,用于释放锁时验证,null表示获取失败
+     */
+    public String lock(String lockKey) {
+        return lock(lockKey, 30000, 10000);
+    }
+
+    /**
+     * 获取分布式锁
+     * @param lockKey 锁的标识
+     * @param expireTime 锁的过期时间(毫秒)
+     * @param acquireTimeout 获取锁的超时时间(毫秒)
+     * @return 锁的value值,用于释放锁时验证,null表示获取失败
+     */
+    public String lock(String lockKey, long expireTime, long acquireTimeout) {
+        // 生成唯一标识,用于释放锁时验证
+        String identifier = UUID.randomUUID().toString();
+        // 完整的锁键
+        String lockKeyWithPrefix = "dining:lock:" + lockKey;
+        // 超时时间戳
+        long endTime = System.currentTimeMillis() + acquireTimeout;
+
+        while (System.currentTimeMillis() < endTime) {
+            // 尝试获取锁:SET NX PX
+            Boolean success = stringRedisTemplate.opsForValue()
+                    .setIfAbsent(lockKeyWithPrefix, identifier, expireTime, TimeUnit.MILLISECONDS);
+
+            if (Boolean.TRUE.equals(success)) {
+                // 获取锁成功
+                return identifier;
+            }
+
+            try {
+                // 短暂休眠,避免频繁尝试
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                return null;
+            }
+        }
+
+        // 获取锁超时
+        return null;
+    }
+
+    /**
+     * 释放分布式锁
+     * @param lockKey 锁的标识
+     * @param identifier 锁的value值,用于验证
+     * @return 是否释放成功
+     */
+    public boolean unlock(String lockKey, String identifier) {
+        if (identifier == null) {
+            return false;
+        }
+
+        String lockKeyWithPrefix = "dining:lock:" + lockKey;
+
+        // 使用Lua脚本确保释放锁的原子性
+        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
+                "return redis.call('del', KEYS[1]) " +
+                "else " +
+                "return 0 " +
+                "end";
+
+        RedisScript<Long> script = new DefaultRedisScript<>(luaScript, Long.class);
+        Long result = stringRedisTemplate.execute(script,
+                Collections.singletonList(lockKeyWithPrefix),
+                identifier);
+
+        return result != null && result > 0;
+    }
+
+}
+

+ 34 - 0
alien-dining/src/main/java/shop/alien/dining/config/CustomAccessDeniedHandler.java

@@ -0,0 +1,34 @@
+package shop.alien.dining.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.web.access.AccessDeniedHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class CustomAccessDeniedHandler implements AccessDeniedHandler {
+
+    @Override
+    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
+            throws IOException {
+        response.setStatus(HttpStatus.FORBIDDEN.value());
+        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+        response.setCharacterEncoding("UTF-8");
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("code", 403);
+        result.put("msg", "权限不足,无法访问该接口");
+        result.put("detail", e.getMessage());
+
+        new ObjectMapper().writeValue(response.getWriter(), result);
+    }
+}
+

+ 59 - 0
alien-dining/src/main/java/shop/alien/dining/config/DruidConfig.java

@@ -0,0 +1,59 @@
+package shop.alien.dining.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.support.http.StatViewServlet;
+import com.alibaba.druid.support.http.WebStatFilter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.sql.DataSource;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Druid配置
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4 9:13
+ */
+@Configuration
+public class DruidConfig {
+
+    @ConfigurationProperties(prefix = "spring.datasource.druid")
+    @Bean
+    public DataSource druid() {
+        return new DruidDataSource();
+    }
+
+    //配置Druid的监控
+    //1、配置一个管理后台的Servlet
+    @Bean
+    public ServletRegistrationBean statViewServlet() {
+        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
+        Map<String, String> initParams = new HashMap<>();
+        initParams.put("loginUsername", "admin");
+        initParams.put("loginPassword", "alien1234");
+        initParams.put("allow", "");//默认就是允许所有访问
+        //initParams.put("deny","127.0.0.1");
+        bean.setInitParameters(initParams);
+        return bean;
+    }
+
+    //2、配置一个web监控的filter
+    @Bean
+    public FilterRegistrationBean webStatFilter() {
+        FilterRegistrationBean bean = new FilterRegistrationBean();
+        bean.setFilter(new WebStatFilter());
+        Map<String, String> initParams = new HashMap<>();
+        initParams.put("exclusions", "*.js,*.css,/druid/*");
+        bean.setInitParameters(initParams);
+        bean.setUrlPatterns(Arrays.asList("/*"));
+        return bean;
+    }
+}
+

+ 14 - 0
alien-dining/src/main/java/shop/alien/dining/config/MyBatisPlusPageConfig.java

@@ -0,0 +1,14 @@
+package shop.alien.dining.config;
+
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MyBatisPlusPageConfig {
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        return new PaginationInterceptor();
+    }
+}
+

+ 69 - 0
alien-dining/src/main/java/shop/alien/dining/config/SecurityConfig.java

@@ -0,0 +1,69 @@
+package shop.alien.dining.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import shop.alien.dining.filter.PreAuthFilter;
+
+import javax.annotation.Resource;
+
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Resource
+    private CustomAccessDeniedHandler customAccessDeniedHandler;
+
+    @Resource
+    private PreAuthFilter preAuthFilter;
+
+    /**
+     * 核心:跳过前端静态资源的Security过滤(不走过滤器链,性能最优)
+     * 适用于前端打包后的静态资源(JS/CSS/图片/字体等)
+     */
+    @Override
+    public void configure(WebSecurity web) throws Exception {
+        web.ignoring()
+                // 前端静态资源路径(根据实际项目调整)
+                .antMatchers("/static/**", "/css/**", "/js/**", "/images/**", "/fonts/**")
+                // 若前端部署在后端,放行打包后的dist目录(Vue/React打包后)
+                .antMatchers("/dist/**", "/public/**")
+                // 放行Swagger(若有)
+                .antMatchers("/swagger-ui/**", "/v3/api-docs/**","/doc.html","/webjars/**");
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                // 1. 禁用CSRF(前后端分离无Cookie,无需保护)
+                .csrf().disable()
+                // 2. 禁用Session(无状态,仅授权)
+                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+                .and()
+                // 3. 关闭所有默认认证入口(表单登录、HTTP Basic)
+                .formLogin().disable()
+                .httpBasic().disable()
+                // 4. 禁用注销
+                .logout().disable()
+                // 5. 配置授权规则
+                .authorizeRequests()
+                // 其他接口:需任意认证(即请求头带X-User-Roles)
+                .anyRequest().permitAll()
+                .and()
+                // 6. 添加自定义预认证过滤器(核心:解析权限)
+                .addFilterBefore(preAuthFilter, UsernamePasswordAuthenticationFilter.class)
+                // 7. 配置授权异常处理器
+                .exceptionHandling().accessDeniedHandler(customAccessDeniedHandler);
+
+        // 允许跨域
+        http.cors();
+    }
+
+}
+

+ 58 - 0
alien-dining/src/main/java/shop/alien/dining/config/SwaggerConfig.java

@@ -0,0 +1,58 @@
+package shop.alien.dining.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * swagger配置类
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4 9:17
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+    /**
+     * controller扫描
+     *
+     * @return controller扫描
+     */
+    @Bean
+    public Docket docket() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .groupName("微信点餐Api服务")
+                .select()
+                .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+    /**
+     * Api标题信息
+     *
+     * @return api标题Api标题信息
+     */
+    public ApiInfo apiInfo() {
+        Contact contact = new Contact("爱丽恩", "https://www.alien.shop", "");
+        return new ApiInfoBuilder()
+                .title("微信点餐Api服务")
+                .description("微信点餐Api服务")
+                .license("爱丽恩")
+                .contact(contact)
+                .termsOfServiceUrl("https://www.alien.shop")
+                .version("1.0.0")
+                .build();
+    }
+}
+

+ 19 - 0
alien-dining/src/main/java/shop/alien/dining/config/WebConfig.java

@@ -0,0 +1,19 @@
+package shop.alien.dining.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+
+    @Override
+    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
+        // 如果需要TokenInfoArgumentResolver,可以从alien-util中引入
+        // resolvers.add(new TokenInfoArgumentResolver());
+    }
+}
+

+ 41 - 0
alien-dining/src/main/java/shop/alien/dining/controller/DiningMenuController.java

@@ -0,0 +1,41 @@
+package shop.alien.dining.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+
+/**
+ * 点餐菜单控制器
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+@Slf4j
+@Api(tags = {"微信点餐-菜单管理(用户端)"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/dining/menu")
+@RequiredArgsConstructor
+public class DiningMenuController {
+
+    @ApiOperation(value = "获取菜单列表", notes = "获取点餐菜单列表")
+    @GetMapping("/list")
+    public R list() {
+        // TODO: 实现菜单列表查询逻辑
+        return R.ok("菜单列表功能待实现");
+    }
+
+    @ApiOperation(value = "获取菜单详情", notes = "根据菜单ID获取菜单详情")
+    @GetMapping("/{id}")
+    public R getById(@PathVariable Long id) {
+        // TODO: 实现菜单详情查询逻辑
+        return R.ok("菜单详情功能待实现");
+    }
+}
+

+ 48 - 0
alien-dining/src/main/java/shop/alien/dining/enums/OrderStatus.java

@@ -0,0 +1,48 @@
+package shop.alien.dining.enums;
+
+/**
+ * 订单状态枚举
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+public enum OrderStatus {
+    /**
+     * 待支付
+     */
+    PENDING_PAYMENT(0, "待支付"),
+    /**
+     * 已支付
+     */
+    PAID(1, "已支付"),
+    /**
+     * 制作中
+     */
+    PREPARING(2, "制作中"),
+    /**
+     * 已完成
+     */
+    COMPLETED(3, "已完成"),
+    /**
+     * 已取消
+     */
+    CANCELLED(4, "已取消");
+
+    private final Integer code;
+    private final String desc;
+
+    OrderStatus(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+}
+

+ 17 - 0
alien-dining/src/main/java/shop/alien/dining/feign/DiningServiceFeign.java

@@ -0,0 +1,17 @@
+package shop.alien.dining.feign;
+
+import org.springframework.cloud.openfeign.FeignClient;
+
+/**
+ * 点餐服务Feign客户端
+ * 用于调用其他微服务
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+@FeignClient(name = "dining-service", path = "/api")
+public interface DiningServiceFeign {
+    // TODO: 定义需要调用的其他服务接口
+}
+

+ 67 - 0
alien-dining/src/main/java/shop/alien/dining/filter/PreAuthFilter.java

@@ -0,0 +1,67 @@
+package shop.alien.dining.filter;
+
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 预认证过滤器
+ * 用于处理微信点餐系统的权限验证
+ */
+@Component
+public class PreAuthFilter extends OncePerRequestFilter {
+    // 自定义权限请求头(前端传递角色/权限)
+    private static final String USER_TOKEN_HEADER = "Authorization";
+    private static final String PLATFORM_TYPE_HEADER = "X-platform-type";
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
+            throws ServletException, IOException {
+        // 1. 从请求头获取token信息
+        String token = request.getHeader(USER_TOKEN_HEADER);
+        String platformType = request.getHeader(PLATFORM_TYPE_HEADER);
+
+        /**
+         * 如果不是平台端请求,直接放行,加admin权放行
+         */
+        if(null == platformType || !"platform".equalsIgnoreCase(platformType)) {
+            UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
+                    "notPlatformUser", // 无认证场景,用户名可自定义
+                    null, // 无需密码,置空
+                    AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", "ROLE_ADMIN")));// 权限列表
+            chain.doFilter(request, response);
+            return;
+        }
+
+        if (token == null) {
+            // 无权限头:视为匿名用户(仅能访问公开接口)
+            chain.doFilter(request, response);
+            return;
+        }
+
+        // TODO: 根据实际需求解析token并设置权限
+        // 2. 解析角色为权限集合(Spring Security 角色需以ROLE_开头)
+        // 3. 构建认证令牌
+        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
+                "diningUser", // 用户名
+                null, // 无需密码,置空
+                AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", "ROLE_USER")));// 权限列表
+        // 4. 设置请求上下文
+        authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+        // 5. 将认证信息存入SecurityContext(标记用户已"认证",仅用于授权)
+        SecurityContextHolder.getContext().setAuthentication(authToken);
+
+        // 继续执行过滤器链
+        chain.doFilter(request, response);
+    }
+}
+

+ 26 - 0
alien-dining/src/main/java/shop/alien/dining/listener/RedisKeyExpirationListener.java

@@ -0,0 +1,26 @@
+package shop.alien.dining.listener;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.connection.MessageListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * Redis键过期监听器
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+@Slf4j
+@Component
+public class RedisKeyExpirationListener implements MessageListener {
+
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        String expiredKey = message.toString();
+        log.info("Redis键过期: {}", expiredKey);
+        // TODO: 实现键过期后的业务逻辑
+    }
+}
+

+ 13 - 0
alien-dining/src/main/java/shop/alien/dining/service/DiningMenuService.java

@@ -0,0 +1,13 @@
+package shop.alien.dining.service;
+
+/**
+ * 点餐菜单服务接口
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+public interface DiningMenuService {
+    // TODO: 定义菜单相关业务方法
+}
+

+ 21 - 0
alien-dining/src/main/java/shop/alien/dining/service/impl/DiningMenuServiceImpl.java

@@ -0,0 +1,21 @@
+package shop.alien.dining.service.impl;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import shop.alien.dining.service.DiningMenuService;
+
+/**
+ * 点餐菜单服务实现类
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DiningMenuServiceImpl implements DiningMenuService {
+    // TODO: 实现菜单相关业务逻辑
+}
+

+ 31 - 0
alien-dining/src/main/java/shop/alien/dining/strategy/payment/PaymentStrategy.java

@@ -0,0 +1,31 @@
+package shop.alien.dining.strategy.payment;
+
+import java.math.BigDecimal;
+
+/**
+ * 支付策略接口
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+public interface PaymentStrategy {
+    /**
+     * 支付
+     *
+     * @param amount 支付金额
+     * @param orderId 订单ID
+     * @return 支付结果
+     */
+    String pay(BigDecimal amount, Long orderId);
+
+    /**
+     * 退款
+     *
+     * @param amount 退款金额
+     * @param orderId 订单ID
+     * @return 退款结果
+     */
+    String refund(BigDecimal amount, Long orderId);
+}
+

+ 34 - 0
alien-dining/src/main/java/shop/alien/dining/strategy/payment/impl/WeChatPaymentStrategyImpl.java

@@ -0,0 +1,34 @@
+package shop.alien.dining.strategy.payment.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import shop.alien.dining.strategy.payment.PaymentStrategy;
+
+import java.math.BigDecimal;
+
+/**
+ * 微信支付策略实现
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+@Slf4j
+@Component
+public class WeChatPaymentStrategyImpl implements PaymentStrategy {
+
+    @Override
+    public String pay(BigDecimal amount, Long orderId) {
+        // TODO: 实现微信支付逻辑
+        log.info("微信支付: 订单ID={}, 金额={}", orderId, amount);
+        return "微信支付成功";
+    }
+
+    @Override
+    public String refund(BigDecimal amount, Long orderId) {
+        // TODO: 实现微信退款逻辑
+        log.info("微信退款: 订单ID={}, 金额={}", orderId, amount);
+        return "微信退款成功";
+    }
+}
+

+ 16 - 0
alien-dining/src/main/java/shop/alien/dining/util/WeChatPayUtil.java

@@ -0,0 +1,16 @@
+package shop.alien.dining.util;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 微信支付工具类
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4
+ */
+@Slf4j
+public class WeChatPayUtil {
+    // TODO: 实现微信支付相关工具方法
+}
+

+ 26 - 0
alien-dining/src/main/resources/bootstrap-bug.yml

@@ -0,0 +1,26 @@
+spring:
+  application:
+    name: alien-dining
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: 120.26.186.130:8848
+        username: dev
+        password: Alien123456
+
+      #配置中心
+      config:
+        enabled: true
+        refresh-enabled: true
+        server-addr: 120.26.186.130:8848
+        username: dev
+        password: Alien123456
+        group: DEFAULT_GROUP
+        file-extension: yml
+        shared-configs:
+          - data-id: common.yml
+            group: DEFAULT_GROUP
+            refresh: true
+

+ 26 - 0
alien-dining/src/main/resources/bootstrap-dev.yml

@@ -0,0 +1,26 @@
+spring:
+  application:
+    name: alien-dining
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: 120.26.186.130:8848
+        username: dev
+        password: Alien123456
+
+      #配置中心
+      config:
+        enabled: true
+        refresh-enabled: true
+        server-addr: 120.26.186.130:8848
+        username: dev
+        password: Alien123456
+        group: DEFAULT_GROUP
+        file-extension: yml
+        shared-configs:
+          - data-id: common.yml
+            group: DEFAULT_GROUP
+            refresh: true
+

+ 26 - 0
alien-dining/src/main/resources/bootstrap-prod.yml

@@ -0,0 +1,26 @@
+spring:
+  application:
+    name: alien-dining
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: 120.26.186.130:8848
+        username: dev
+        password: Alien123456
+
+      #配置中心
+      config:
+        enabled: true
+        refresh-enabled: true
+        server-addr: 120.26.186.130:8848
+        username: dev
+        password: Alien123456
+        group: DEFAULT_GROUP
+        file-extension: yml
+        shared-configs:
+          - data-id: common.yml
+            group: DEFAULT_GROUP
+            refresh: true
+

+ 26 - 0
alien-dining/src/main/resources/bootstrap-test.yml

@@ -0,0 +1,26 @@
+spring:
+  application:
+    name: alien-dining
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: 120.26.186.130:8848
+        username: dev
+        password: Alien123456
+
+      #配置中心
+      config:
+        enabled: true
+        refresh-enabled: true
+        server-addr: 120.26.186.130:8848
+        username: dev
+        password: Alien123456
+        group: DEFAULT_GROUP
+        file-extension: yml
+        shared-configs:
+          - data-id: common.yml
+            group: DEFAULT_GROUP
+            refresh: true
+

+ 26 - 0
alien-dining/src/main/resources/bootstrap-uat.yml

@@ -0,0 +1,26 @@
+spring:
+  application:
+    name: alien-dining
+
+  cloud:
+    nacos:
+      #注册中心
+      discovery:
+        server-addr: 120.26.186.130:8848
+        username: dev
+        password: Alien123456
+
+      #配置中心
+      config:
+        enabled: true
+        refresh-enabled: true
+        server-addr: 120.26.186.130:8848
+        username: dev
+        password: Alien123456
+        group: DEFAULT_GROUP
+        file-extension: yml
+        shared-configs:
+          - data-id: common.yml
+            group: DEFAULT_GROUP
+            refresh: true
+

+ 4 - 0
alien-dining/src/main/resources/bootstrap.yml

@@ -0,0 +1,4 @@
+spring:
+  profiles:
+    active: dev
+

+ 178 - 0
alien-dining/src/main/resources/logback-spring.xml

@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
+<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
+<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
+<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
+<!-- 该信息是由于设置了当配置文件变化时重新加载,所以每当达到扫描时间的时候就会检查配置文件是否错误。但是由于一般配置文件都放在了JAR包中,
+    而扫描的时候无法扫描JAR包内,因此会提示没有可以检查的文件,所以每隔一段时间就输出一次-->
+<configuration scan="false" scanPeriod="60 seconds" debug="true">
+<!--    <contextName>logback-spring</contextName>-->
+
+    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使"${}"来使用变量。 -->
+    <!-- 定义全局参数常量 -->
+    <property name="log.level" value="debug"/>
+    <property name="log.maxHistory" value="30"/><!-- 30表示30个 -->
+    <springProperty scope="context" name="logging.path" source="logging.path"  defaultValue="C:/project/ext/log"/>
+    <!--输出文件前缀-->
+    <property name="FILENAME" value="alien-dining"/>
+
+    <!--0. 日志格式和颜色渲染 -->
+    <!-- 彩色日志依赖的渲染类 -->
+    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
+    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
+    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
+
+    <!-- 文件输出格式 -->
+    <property name="FILE_LOG_PATTERN" value="[%d{MM/dd HH:mm:ss.SSS}][%-10.10thread][%-5level][%-40.40c{1}:%5line]:[%15method] || %m%n"/>
+    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+
+    <!--1. 输出到控制台-->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>${log.level}</level>
+        </filter>
+        <encoder>
+            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+            <!-- 设置字符集 -->
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <!--2. 输出到文档-->
+    <!-- DEBUG 日志 -->
+    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 当前的日志文件存放路径 -->
+        <file>${logging.path}/DEBUG.log</file>
+        <!-- 日志滚动策略 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 历史日志文件的存放路径和名称 -->
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_DEBUG.log.gz</fileNamePattern>
+            <!-- 日志文件最大的保存历史 数量-->
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <!--日志文件最大的大小-->
+        <!--        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">-->
+        <!--            <MaxFileSize>10MB</MaxFileSize>-->
+        <!--        </triggeringPolicy>-->
+        <!-- 此日志文档只记录debug级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>DEBUG</level>
+            <onMatch>ACCEPT</onMatch>  <!-- 用过滤器,只接受DEBUG级别的日志信息,其余全部过滤掉 -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- INFO 日志 -->
+    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logging.path}/INFO.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_INFO.log.gz</fileNamePattern>
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- WARN 日志 -->
+    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logging.path}/WARN.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_WARN.log.gz</fileNamePattern>
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>WARN</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logging.path}/ERROR.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logging.path}/%d{yyyy-MM-dd}_${FILENAME}_ERROR.log.gz</fileNamePattern>
+            <maxHistory>${log.maxHistory}</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--
+      <logger>用来设置某一个包或者具体的某一个类的日志打印级别、
+      以及指定<appender>。<logger>仅有一个name属性,
+      一个可选的level和一个可选的addtivity属性。
+      name:用来指定受此logger约束的某一个包或者具体的某一个类。
+      level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+         还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
+         如果未设置此属性,那么当前logger将会继承上级的级别。
+      addtivity:是否向上级logger传递打印信息。默认是true。
+      <logger name="org.springframework.web" level="info"/>
+      <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
+    -->
+
+    <!--
+      使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
+      第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
+      第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
+      【logging.level.org.mybatis=debug logging.level.dao=debug】
+     -->
+    <!-- mybatis显示sql,修改此处扫描包名 -->
+
+
+    <!--
+      root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
+      level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+      不能设置为INHERITED或者同义词NULL。默认是DEBUG
+      可以包含零个或多个元素,标识这个appender将会添加到这个logger。
+    -->
+
+    <logger name="springfox.documentation.spring.web.readers.operation.CachingOperationNameGenerator" level="WARN"/>
+    <logger name="org.springframework.security.web.DefaultSecurityFilterChain " level="WARN"/>
+    <logger name="com.netflix.config.sources.URLConfigurationSource " level="WARN"/>
+
+    <!-- 4. 最终的策略 -->
+    <!-- 4.1 开发环境:打印控制台-->
+    <!--打印sql-->
+    <!--    <logger name="com.veryhappy.music.dao" level="debug"/>-->
+
+    <!--打印log-->
+    <root level="info">
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="DEBUG_FILE"/>
+        <appender-ref ref="INFO_FILE"/>
+        <appender-ref ref="WARN_FILE"/>
+        <appender-ref ref="ERROR_FILE"/>
+    </root>
+
+    <!--   4.2 生产环境:输出到文档-->
+    <springProfile name="pro">
+        <root level="info">
+            <appender-ref ref="CONSOLE"/>
+            <appender-ref ref="DEBUG_FILE"/>
+            <appender-ref ref="INFO_FILE"/>
+            <appender-ref ref="ERROR_FILE"/>
+            <appender-ref ref="WARN_FILE"/>
+        </root>
+    </springProfile>
+</configuration>
+

+ 3 - 0
alien-entity/src/main/java/shop/alien/entity/result/R.java

@@ -204,4 +204,7 @@ public class R<T> implements Serializable {
         return flag ? success(APPConstant.DEFAULT_SUCCESS_MESSAGE) : fail(APPConstant.DEFAULT_FAILURE_MESSAGE);
     }
 
+    public static R ok(String 菜单列表功能待实现) {
+        return R.ok("菜单列表功能待实现");
+    }
 }

+ 1 - 0
pom.xml

@@ -19,6 +19,7 @@
         <module>alien-second</module>
         <module>alien-lawyer</module>
         <module>alien-store-platform</module>
+        <module>alien-dining</module>
     </modules>
     <!--pom工程作用:1、管理版本;2、聚合工程-->
     <packaging>pom</packaging>