Jelajahi Sumber

修改短信发送规则(宽松)

Mure 3 minggu lalu
induk
melakukan
ea6637bac3
21 mengubah file dengan 319 tambahan dan 47 penghapusan
  1. 91 0
      configuration/docker/compose/docker-compose.yml
  2. 5 5
      configuration/nginx/conf.d/ai.daogu.api.conf
  3. 2 1
      configuration/nginx/conf.d/ai.daogu.manage.conf
  4. 12 12
      db/ai_material.sql
  5. 4 1
      db/ai_material_category.sql
  6. 12 6
      db/ai_material_tag.sql
  7. 1 1
      db/sys_user.sql
  8. 2 1
      src/main/java/com/backendsys/modules/material/controller/MaterialCategoryController.java
  9. 3 1
      src/main/java/com/backendsys/modules/material/controller/MaterialController.java
  10. 3 2
      src/main/java/com/backendsys/modules/material/controller/MaterialLoraController.java
  11. 2 1
      src/main/java/com/backendsys/modules/material/controller/MaterialTagController.java
  12. 10 0
      src/main/java/com/backendsys/modules/material/controller/MaterialUserController.java
  13. 28 0
      src/main/java/com/backendsys/modules/material/entity/MaterialUser.java
  14. 4 0
      src/main/java/com/backendsys/modules/material/service/MaterialUserService.java
  15. 122 4
      src/main/java/com/backendsys/modules/material/service/impl/MaterialUserServiceImpl.java
  16. 1 1
      src/main/java/com/backendsys/modules/sms/entity/Sms.java
  17. 4 1
      src/main/java/com/backendsys/modules/sms/service/impl/SmsServiceImpl.java
  18. 6 0
      src/main/java/com/backendsys/modules/system/service/SysAuthService.java
  19. 5 3
      src/main/java/com/backendsys/modules/system/service/impl/SysAuthServiceImpl.java
  20. 0 7
      src/main/java/com/backendsys/modules/system/service/impl/SysUserServiceImpl.java
  21. 2 0
      src/main/resources/mapper/system/SysUserDao.xml

+ 91 - 0
configuration/docker/compose/docker-compose.yml

@@ -0,0 +1,91 @@
+version: "3.9"
+
+# Docker for Ubuntu22.04 configuration
+# - mysql 8.0.23
+# - redis 6.2
+
+# -- 配置 -----------------------------
+# docker-compose down
+# docker-compose up -d
+
+# 1.首次启动前创建 (MySQL)
+# mkdir -p /opt/mysql/{data,config} /opt/mysql-files
+# chmod -R 777 /opt/mysql
+
+# 拷贝 my.cnf 配置文件 (MySQL)
+# * 首次启动,不要带 my.cnf 映射,要先把 my.cnf 文件拷出来
+# docker cp mysql8:/etc/mysql/my.cnf /opt/mysql/config
+# chmod 644 /opt/mysql/config/my.cnf
+# (保证 my.cnf 必须是文件,不是目录)
+
+# 3.首次启动前创建(Redis)
+# mkdir -p /opt/redis/{data,logs}
+# chmod -R 777 /opt/redis
+
+# 4.首次启动前创建(Nginx)
+# mkdir -p /opt/nginx/{www,log}
+# chmod -R 777 /opt/nginx
+
+# 拷贝 nginx.conf 配置文件 (Nginx)
+# docker cp nginx:/etc/nginx/conf.d /opt/nginx
+# docker cp nginx:/etc/nginx/nginx.conf /opt/nginx/nginx.conf
+# docker cp nginx:/usr/share/nginx/html /opt/nginx/www/html
+# -------------------------------------
+
+services:
+
+  # -- MySQL8.0 -----------------------
+  mysql8:
+    image: mysql:8.0.23
+    container_name: mysql8
+    restart: always
+    environment:
+      MYSQL_ROOT_PASSWORD: fiPxHGFJldDC
+    ports:
+      - "3306:3306"
+    volumes:
+      - /opt/mysql/data:/var/lib/mysql
+      # 首次复制 (docker cp)
+      # - /opt/mysql/config/my.cnf:/etc/mysql/my.cnf:ro
+      - /opt/mysql-files:/var/lib/mysql-files
+    command:
+      - mysqld
+      - --sql-mode=STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
+
+  # -- Redis6.2 -----------------------
+  redis:
+    image: redis:6.2               # 官方完整版,非 Alpine
+    container_name: redis62
+    restart: always
+    ports:
+      - "127.0.0.1:6388:6388"       # 仅宿主机 127.0.0.1:6388 可访问
+    volumes:
+      - /opt/redis/data:/data       # 数据持久化
+      - /opt/redis/logs:/logs       # 日志外挂
+    environment:
+      - TZ=UTC                      # UTC时区 (国内时区: Asia/Shanghai)
+    command: >
+      redis-server
+      --port 6388
+      --bind 127.0.0.1
+      --requirepass 123456
+      --appendonly yes
+      --loglevel warning
+      --logfile /logs/redis.log
+
+  # -- Nginx --------------------------
+  nginx:
+    image: nginx:1.26.3
+    container_name: nginx
+    restart: always
+    ports:
+      - "80:80"
+      - "443:443"
+    volumes:
+      # 首次复制 (docker cp)
+      # - /opt/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
+      # - /opt/nginx/www/html:/usr/share/nginx/html:ro
+      # - /opt/nginx/conf.d:/etc/nginx/conf.d:ro
+      - /opt/nginx/log:/var/log/nginx
+    environment:
+      - TZ=UTC

+ 5 - 5
configuration/nginx/conf.d/ai.daogu.api.conf

@@ -1,4 +1,4 @@
-upstream myapi {
+upstream DAOGUAPI {
     ip_hash;
     server 127.0.0.1:48080;
 }
@@ -14,7 +14,7 @@ server {
     }
 
     location / {
-        proxy_pass http://myapi;
+        proxy_pass http://DAOGUAPI;
         proxy_http_version 1.1;
         proxy_connect_timeout 4s;
         proxy_read_timeout 60s;
@@ -30,7 +30,7 @@ server {
 
     # 接口开放监听 (SSE)
     location /api/sse/ {
-        proxy_pass http://myapi/api/sse/;
+        proxy_pass http://DAOGUAPI/api/sse/;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_read_timeout 300s;
@@ -54,7 +54,7 @@ server {
     ssl_prefer_server_ciphers  on;
 
     location / {
-        proxy_pass http://myapi;
+        proxy_pass http://DAOGUAPI;
         proxy_http_version 1.1;
         proxy_connect_timeout 4s;
         proxy_read_timeout 60s;
@@ -70,7 +70,7 @@ server {
 
     # 接口开放监听 (SSE)
     location /api/sse/ {
-        proxy_pass http://myapi/api/sse/;
+        proxy_pass http://DAOGUAPI/api/sse/;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_read_timeout 300s;

+ 2 - 1
configuration/nginx/conf.d/ai.daogu.manage.conf

@@ -36,7 +36,7 @@ server {
         try_files $uri $uri/ /index.html;
         # 允许跨域
         add_header  Access-Control-Allow-Headers *;
-        add_header Access-Control-Allow-Origin $http_origin always;
+        add_header  Access-Control-Allow-Origin $http_origin always;
         add_header  Access-Control-Allow-Methods 'GET,POST,OPTIONS';
         add_header  Access-Control-Allow-Credentials 'true';
     }
@@ -67,6 +67,7 @@ server {
 server {
     listen       443 ssl;
     server_name  ai.manage.daoguyujia.com manage.daogu.ai;
+    client_max_body_size 500M;
 
     ssl_certificate      /root/.acme.sh/ai.manage.daoguyujia.com/fullchain.cer;
     ssl_certificate_key  /root/.acme.sh/ai.manage.daoguyujia.com/ai.manage.daoguyujia.com.key;

+ 12 - 12
db/ai_material.sql

@@ -26,17 +26,17 @@ CREATE TABLE `ai_material` (
 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='素材表';
 
 INSERT INTO ai_material(user_id, category_id, tag_ids, material_name, image_thumb_url, image_url, file_url, is_copyright, create_time) VALUES
-    (1, 1, '1,2', '御姐魔女-1', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:01'),
-    (1, 1, '1,2', '御姐魔女-2', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:02'),
-    (1, 1, '1,2', '御姐魔女-3', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:03'),
-    (1, 1, '1,2', '御姐魔女-4', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:04'),
-    (1, 1, '1,2', '御姐魔女-5', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:05'),
-    (1, 1, '1,2', '御姐魔女-6', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:06'),
-    (1, 1, '1,2', '御姐魔女-7', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:07'),
-    (1, 1, '4,5', '御姐魔女-8', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:08'),
-    (1, 1, '3,4,5', '御姐魔女-9', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:09'),
-    (1, 1, '3', '御姐魔女-10', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:10'),
-    (1, 2, '1,2', '御姐魔女-11', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:11'),
-    (1, 1, '1,2', '竹屋内', 'https://cos.daogu.ai/temp/d2361395-6e39-4a01-8c3b-1c79095f4391.png?imageMogr2/thumbnail/315x180/pad/1/color/I2Y4ZjhmOA==', 'https://cos.daogu.ai/temp/d2361395-6e39-4a01-8c3b-1c79095f4391.png', 'https://cos.daogu.ai/temp/2d0e951d-d894-4f88-8904-bf9f1d4797f5.fla', 1, '2025-07-30 08:30:00')
+    (1, 2, '6', '御姐魔女-1', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:01'),
+    (1, 2, '6', '御姐魔女-2', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:02'),
+    (1, 2, '6', '御姐魔女-3', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:03'),
+    (1, 2, '6', '御姐魔女-4', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:04'),
+    (1, 2, '6', '御姐魔女-5', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:05'),
+    (1, 2, '6', '御姐魔女-6', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:06'),
+    (1, 2, '6', '御姐魔女-7', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:07'),
+    (1, 2, '6', '御姐魔女-8', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:08'),
+    (1, 2, '6', '御姐魔女-9', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:09'),
+    (1, 2, '6', '御姐魔女-10', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:10'),
+    (1, 2, '6', '御姐魔女-11', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.png', 'https://www.xxx.com/xx.fla', 1, '2025-07-28 08:30:11'),
+    (1, 2, '6', '竹屋内', 'https://cos.daogu.ai/temp/d2361395-6e39-4a01-8c3b-1c79095f4391.png?imageMogr2/thumbnail/315x180/pad/1/color/I2Y4ZjhmOA==', 'https://cos.daogu.ai/temp/d2361395-6e39-4a01-8c3b-1c79095f4391.png', 'https://cos.daogu.ai/temp/2d0e951d-d894-4f88-8904-bf9f1d4797f5.fla', 1, '2025-07-30 08:30:00')
 ;
 

+ 4 - 1
db/ai_material_category.sql

@@ -26,6 +26,9 @@ INSERT INTO ai_material_category(lora_id, is_share, category_name, sort) VALUES
     (2, -1,  '人物素材', 7),
     (2, -1,  '场景素材', 6),
     (2, -1,  '异兽素材', 5),
-    (2, -1,  '道具素材', 4)
+    (2, -1,  '道具素材', 4),
+
+    (1, -1, '表情素材', 2),
+    (2, -1,  '表情素材', 3)
 ;
 

+ 12 - 6
db/ai_material_tag.sql

@@ -23,26 +23,25 @@ INSERT INTO ai_material_tag(category_id, tag_name, sort) VALUES
     (1, '阵法特效', 7),
     (1, '全屏特效', 6),
 
-    # 1-沙雕素材-人物素材
+    # 2-沙雕素材-人物素材
     (2, '精品人物', 10),
     (2, '修仙人物', 9),
     (2, '都市人物', 8),
     (2, '历史人物', 7),
-    # 2-沙雕素材-场景素材
+    # 3-沙雕素材-场景素材
     (3, '修仙场景', 10),
     (3, '都市场景', 9),
     (3, '历史场景', 8),
     (3, '8090场景', 7),
     (3, '末日悬疑场景', 6),
-    # 3-沙雕素材-异兽素材
+    # 4-沙雕素材-异兽素材
     (4, '异兽', 10),
     (4, '动物', 9),
-    # 4-沙雕素材-道具素材
+    # 5-沙雕素材-道具素材
     (5, '修仙道具', 10),
     (5, '都市道具', 9),
     (5, '历史道具', 8),
 
-
     # 6-简笔画素材-人物素材
     (6, '精品人物', 10),
     (6, '修仙人物', 9),
@@ -61,7 +60,14 @@ INSERT INTO ai_material_tag(category_id, tag_name, sort) VALUES
     # 9-简笔画素材-道具素材
     (9, '修仙道具', 10),
     (9, '都市道具', 9),
-    (9, '历史道具', 8)
+    (9, '历史道具', 8),
+
+    # 10-沙雕素材-表情素材
+    (10, '男表情', 10),
+    (10, '女表情', 9),
+    # 11-简笔画素材-表情素材
+    (11, '正常眼', 10),
+    (11, '豆豆眼', 9)
 
 ;
 

+ 1 - 1
db/sys_user.sql

@@ -11,7 +11,7 @@ CREATE TABLE `sys_user` (
     `username` VARCHAR(20) COMMENT '用户名',
     `phone` VARCHAR(20) COMMENT '手机号码',
     `phone_area_code` INT COMMENT '手机区号/国家码',
-    `password` VARCHAR(100) NOT NULL COMMENT '密码',
+    `password` VARCHAR(100) COMMENT '密码',
     UNIQUE KEY (`username`),
     UNIQUE KEY (`phone`),
     INDEX `idx_username` (`username`),

+ 2 - 1
src/main/java/com/backendsys/modules/material/controller/MaterialCategoryController.java

@@ -1,5 +1,6 @@
 package com.backendsys.modules.material.controller;
 
+import com.backendsys.modules.common.config.security.annotations.Anonymous;
 import com.backendsys.modules.material.entity.MaterialCategory;
 import com.backendsys.modules.material.service.MaterialCategoryService;
 import com.backendsys.modules.common.utils.Result;
@@ -25,8 +26,8 @@ public class MaterialCategoryController {
         return Result.success().put("data", materialCategoryService.selectMaterialCategoryList(materialCategory));
     }
 
+    @Anonymous
     @Operation(summary = "获取素材分类列表(下拉)")
-//    @PreAuthorize("@sr.hasPermission('20.2')")
     @GetMapping("/api/material/getMaterialCategoryPopover")
     public Result getMaterialCategoryPopover(MaterialCategory materialCategory) {
         return Result.success().put("data", materialCategoryService.selectMaterialCategoryPopover(materialCategory));

+ 3 - 1
src/main/java/com/backendsys/modules/material/controller/MaterialController.java

@@ -1,5 +1,6 @@
 package com.backendsys.modules.material.controller;
 
+import com.backendsys.modules.common.config.security.annotations.Anonymous;
 import com.backendsys.modules.material.entity.Material;
 import com.backendsys.modules.material.service.MaterialService;
 import com.backendsys.modules.common.aspect.SysLog;
@@ -20,8 +21,9 @@ public class MaterialController {
     @Autowired
     private MaterialService materialService;
 
+    @Anonymous
     @Operation(summary = "获取素材列表")
-    @PreAuthorize("@sr.hasPermission('20.1')")
+//    @PreAuthorize("@sr.hasPermission('20.1')")
     @GetMapping("/api/material/getMaterialList")
     public Result getMaterialList(Material material) {
         return Result.success().put("data", materialService.selectMaterialList(material));

+ 3 - 2
src/main/java/com/backendsys/modules/material/controller/MaterialLoraController.java

@@ -1,5 +1,6 @@
 package com.backendsys.modules.material.controller;
 
+import com.backendsys.modules.common.config.security.annotations.Anonymous;
 import com.backendsys.modules.common.utils.Result;
 import com.backendsys.modules.material.entity.MaterialLora;
 import com.backendsys.modules.material.service.MaterialLoraService;
@@ -19,15 +20,15 @@ public class MaterialLoraController {
     @Autowired
     private MaterialLoraService materialLoraService;
 
+    @Anonymous
     @Operation(summary = "获取素材风格列表")
-//    @PreAuthorize("@sr.hasPermission('20.3')")
     @GetMapping("/api/material/getMaterialLoraList")
     public Result getMaterialLoraList(@Validated(MaterialLora.LoraList.class) MaterialLora materialLora) {
         return Result.success().put("data", materialLoraService.selectMaterialLoraList(materialLora));
     }
 
+    @Anonymous
     @Operation(summary = "获取素材风格列表(下拉)")
-//    @PreAuthorize("@sr.hasPermission('20.3')")
     @GetMapping("/api/material/getMaterialLoraPopover")
     public Result getMaterialLoraPopover(@Validated(MaterialLora.LoraList.class) MaterialLora materialLora) {
         return Result.success().put("data", materialLoraService.selectMaterialLoraPopover(materialLora));

+ 2 - 1
src/main/java/com/backendsys/modules/material/controller/MaterialTagController.java

@@ -1,5 +1,6 @@
 package com.backendsys.modules.material.controller;
 
+import com.backendsys.modules.common.config.security.annotations.Anonymous;
 import com.backendsys.modules.material.entity.MaterialTag;
 import com.backendsys.modules.material.service.MaterialTagService;
 import com.backendsys.modules.common.utils.Result;
@@ -26,8 +27,8 @@ public class MaterialTagController {
         return Result.success().put("data", materialTagService.selectMaterialTagList(materialTag));
     }
 
+    @Anonymous
     @Operation(summary = "获取素材标签列表(下拉)")
-    @PreAuthorize("@sr.hasPermission('20.3')")
     @GetMapping("/api/material/getMaterialTagPopover")
     public Result getMaterialTagPopover(@Validated(MaterialTag.TagList.class) MaterialTag materialTag) {
         return Result.success().put("data", materialTagService.selectMaterialTagPopover(materialTag));

+ 10 - 0
src/main/java/com/backendsys/modules/material/controller/MaterialUserController.java

@@ -1,8 +1,10 @@
 package com.backendsys.modules.material.controller;
 
 import com.backendsys.modules.common.aspect.SysLog;
+import com.backendsys.modules.common.config.security.annotations.Anonymous;
 import com.backendsys.modules.common.utils.Result;
 import com.backendsys.modules.material.entity.MaterialAudit;
+import com.backendsys.modules.material.entity.MaterialUser;
 import com.backendsys.modules.material.service.MaterialUserService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -41,5 +43,13 @@ public class MaterialUserController {
         return Result.success().put("data", materialUserService.auditMaterialUser(materialAudit));
     }
 
+    @Anonymous
+    @SysLog("注册成为素材用户 (手机号注册)")
+    @Operation(summary = "注册成为素材用户 (手机号注册)")
+    @PostMapping("/api/material/registerMaterialUser")
+    public Result registerMaterialUser(@Validated(MaterialUser.Register.class) @RequestBody MaterialUser materialUser) {
+        return Result.success().put("data", materialUserService.registerMaterialUser(materialUser));
+    }
+
 
 }

+ 28 - 0
src/main/java/com/backendsys/modules/material/entity/MaterialUser.java

@@ -0,0 +1,28 @@
+package com.backendsys.modules.material.entity;
+
+import com.backendsys.entity.System.SysUserDTO;
+import com.backendsys.entity.validator.Phone;
+import jakarta.validation.constraints.*;
+import lombok.Data;
+
+@Data
+public class MaterialUser {
+
+    public static interface Register{}
+
+    @NotEmpty(message="手机号码不能为空", groups = { Register.class })
+    @Phone(message="手机号码格式不正确", groups = { Register.class })
+    @Size(min = 9, max = 20, message = "手机号码长度在 {min}-{max} 字符", groups = { Register.class })
+    private String phone;
+
+    @NotNull(message="验证码不能为空", groups = { Register.class })
+    @Min(value = 100000, message = "验证码长度是 6 位字符", groups = { Register.class })
+    @Max(value = 999999, message = "验证码长度是 6 位字符", groups = { Register.class })
+    private Integer phone_valid_code;
+
+    @NotNull(message="区号/国家码不能为空", groups = { Register.class })
+    @Max(value = 999999, message = "区号/国家码长度不超过 {value} 字符", groups = { Register.class })
+    private Integer phone_area_code;
+    
+    
+}

+ 4 - 0
src/main/java/com/backendsys/modules/material/service/MaterialUserService.java

@@ -1,6 +1,7 @@
 package com.backendsys.modules.material.service;
 
 import com.backendsys.modules.material.entity.MaterialAudit;
+import com.backendsys.modules.material.entity.MaterialUser;
 import com.backendsys.modules.system.entity.SysUserRole;
 import com.backendsys.utils.response.PageEntity;
 
@@ -18,4 +19,7 @@ public interface MaterialUserService {
     // 审核素材用户
     Map<String, Object> auditMaterialUser(MaterialAudit materialAudit);
 
+    // 注册素材用户 (手机号注册)
+    Map<String, Object> registerMaterialUser(MaterialUser materialUser);
+
 }

+ 122 - 4
src/main/java/com/backendsys/modules/material/service/impl/MaterialUserServiceImpl.java

@@ -1,33 +1,59 @@
 package com.backendsys.modules.material.service.impl;
 
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.bean.BeanUtil;
 import com.backendsys.exception.CustException;
+import com.backendsys.modules.common.config.redis.utils.RedisUtil;
+import com.backendsys.modules.common.config.security.utils.CaptchaUtil;
+import com.backendsys.modules.common.config.security.utils.LockStatusUtil;
 import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.material.entity.MaterialAudit;
+import com.backendsys.modules.material.entity.MaterialUser;
 import com.backendsys.modules.material.service.MaterialUserService;
 import com.backendsys.modules.system.dao.SysUserDao;
 import com.backendsys.modules.system.dao.SysUserInfoDao;
 import com.backendsys.modules.system.dao.SysUserRoleDao;
 import com.backendsys.modules.system.dao.SysUserRoleRelationDao;
-import com.backendsys.modules.system.entity.SysUserDTO;
-import com.backendsys.modules.system.entity.SysUserInfo;
-import com.backendsys.modules.system.entity.SysUserRole;
-import com.backendsys.modules.system.entity.SysUserRoleRelation;
+import com.backendsys.modules.system.entity.*;
+import com.backendsys.modules.system.service.SysAuthService;
+import com.backendsys.modules.system.service.SysCommonService;
+import com.backendsys.modules.system.service.SysUserIntegralService;
 import com.backendsys.modules.system.service.SysUserService;
 import com.backendsys.utils.response.PageEntity;
 import com.backendsys.utils.response.PageInfoResult;
 import com.backendsys.utils.v2.PageUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.env.Environment;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.Arrays;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
 @Service
 public class MaterialUserServiceImpl implements MaterialUserService {
 
+    @Value("${spring.application.name}")
+    private String APPLICATION_NAME;
+    @Value("${tencent.sms.debug}")
+    private String SMS_DEBUG;
+
+    @Autowired
+    private Environment env;
+    @Autowired
+    private LockStatusUtil lockStatusUtil;
+    @Autowired
+    private CaptchaUtil captchaUtil;
+    @Autowired
+    private RedisUtil redisUtil;
+
     @Autowired
     private SysUserDao sysUserDao;
     @Autowired
@@ -37,8 +63,14 @@ public class MaterialUserServiceImpl implements MaterialUserService {
     @Autowired
     private SysUserRoleRelationDao sysUserRoleRelationDao;
 
+    @Autowired
+    private SysAuthService sysAuthService;
     @Autowired
     private SysUserService sysUserService;
+    @Autowired
+    private SysCommonService sysCommonService;
+    @Autowired
+    private SysUserIntegralService sysUserIntegralService;
 
     /**
      * 获取素材用户列表 ({ invite_code = Material })
@@ -110,4 +142,90 @@ public class MaterialUserServiceImpl implements MaterialUserService {
         return Map.of("user_id", materialAudit.getUser_id());
     }
 
+
+    // 注册素材用户 (手机号注册)
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> registerMaterialUser(MaterialUser materialUser) {
+
+        // 判断是否允许注册
+        // [系统配置] 是否允许系统用户注册
+        Boolean SYSTEM_USER_ALLOW_REGISTER = Convert.toBool(sysCommonService.getCommonByTag("SYSTEM_USER_ALLOW_REGISTER"));
+        if (!SYSTEM_USER_ALLOW_REGISTER) throw new CustException("系统已禁止注册");
+
+        String phone = materialUser.getPhone();
+        Integer phoneAreaCode = materialUser.getPhone_area_code();
+        Integer phoneValidCode = materialUser.getPhone_valid_code();
+
+        String activeProfile = env.getActiveProfiles()[0];
+
+        //if (!"local".equals(activeProfile)) {
+        //    // 判断是否处于登录错误的冻结状态 (2分钟内错误5次,则出现冻结提示)
+        //    lockStatusUtil.checkLockStatus(APPLICATION_NAME + "-register-error", phone);
+        //    // 判断图形验证码是否正确
+        //    if (!captchaUtil.isCaptchaValid(captcha, httpRequestUtil.getKaptchaKey())) {
+        //        loginFail("验证码错误", username, false);
+        //        return null;
+        //    }
+        //}
+
+
+        // 判断短信验证码是否正确
+        if (!"local".equals(activeProfile)) {
+            String redisKey = APPLICATION_NAME + "-sms-" + phone;
+            Integer smsCode = redisUtil.getCacheObject(redisKey);
+            // 判断是否发送验证码
+            if ("false".equals(SMS_DEBUG) && smsCode == null) throw new CustException("请先发送短信验证码");
+            // 判断短信验证码是否错误
+            if ("false".equals(SMS_DEBUG) && !smsCode.equals(phoneValidCode)) sysAuthService.loginFail("短信验证码错误", phone, true);
+        }
+
+
+        // [查询] 判断手机号是否存在 (如果存在就直接登录,返回Token)
+        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SysUser::getPhone, phone).eq(SysUser::getPhone_area_code, phoneAreaCode);
+        SysUser sysUserDetail = sysUserDao.selectOne(queryWrapper);
+        if (sysUserDetail != null) {
+            // [已注册] 返回登录信息 (并且7天免登录)
+            SysUserInfo sysUserInfo = sysAuthService.loginSuccess(sysUserDetail.getId(), 1);
+            Map<String, Object> response = BeanUtil.beanToMap(sysUserInfo);
+            response.put("is_register", false);
+            return response;
+            //
+        } else {
+            // [未注册] 通过校验并注册 (注册成功后,返回登录信息)
+
+            // 注册
+            SysUserDTO registerEntity = new SysUserDTO();
+            registerEntity.setUsername(phone);
+            registerEntity.setPhone(phone);
+            registerEntity.setPhone_area_code(phoneAreaCode);
+            registerEntity.setInvite_code("Material");
+
+            // 注册时,使用 [素材游客] 权限 (MATERIAL_GUEST)
+            String role_sign = "MATERIAL_GUEST";
+            LambdaQueryWrapper<SysUserRole> wrapperRole = new LambdaQueryWrapper<>();
+            wrapperRole.eq(SysUserRole::getRole_sign, role_sign);
+            SysUserRole roleDetail = sysUserRoleDao.selectOne(wrapperRole);
+            registerEntity.setRole_id(Arrays.asList(roleDetail.getRole_id()));
+
+            // 注册时,审核状态为 待审核 (-1拒绝, 1待审核, 2审核通过)
+            registerEntity.setAudit_status(2);
+            // 随机昵称 (6位)
+            registerEntity.setNickname("用户" + RandomUtil.randomStringUpper(6));
+
+            // 创建用户
+            sysUserDao.insertUser(registerEntity);
+
+            // 初始化用户积分
+            sysUserIntegralService.init(registerEntity.getId());
+
+            // [DB] 查询用户信息
+            SysUserInfo sysUserInfo = sysAuthService.loginSuccess(registerEntity.getId(), 1);
+            Map<String, Object> response = BeanUtil.beanToMap(sysUserInfo);
+            response.put("is_register", true);
+            return response;
+
+        }
+    }
 }

+ 1 - 1
src/main/java/com/backendsys/modules/sms/entity/Sms.java

@@ -14,7 +14,7 @@ public class Sms {
     public static interface Send{}
     private Long id;
     @RangeStringArray(message="短信类型有误,范围应是(login:登录, register:注册, forgotPassword: 忘记密码)", value = {"login", "register", "forgotPassword"}, groups = { Send.class })
-    @NotEmpty(message="短信类型不能为空", groups = { Send.class })
+    //@NotEmpty(message="短信类型不能为空", groups = { Send.class })
     private String origin;
     @NotEmpty(message="手机号码不能为空", groups = { Send.class })
     @Size(max = 20, message = "手机号码长度不超过 {max} 字符", groups = { Send.class })

+ 4 - 1
src/main/java/com/backendsys/modules/sms/service/impl/SmsServiceImpl.java

@@ -2,6 +2,7 @@ package com.backendsys.modules.sms.service.impl;
 
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.backendsys.exception.CustException;
@@ -86,7 +87,9 @@ public class SmsServiceImpl implements com.backendsys.modules.sms.service.SmsSer
 
         // -- 发送成功 --------------------------------------------------
         // [Redis] 将 来源+手机号码 为标识,将验证码存入缓存
-        String redisKey = APPLICATION_NAME + "-sms-" + origin + "-" + phone;
+        String originStr = StrUtil.isNotBlank(origin) ? (origin + "-") : "";
+        String redisKey = APPLICATION_NAME + "-sms-" + originStr + phone;
+
         redisUtil.setCacheObject(redisKey, smsCode, smsExpire, TimeUnit.MINUTES);
 
         // [插入] 新增短信记录

+ 6 - 0
src/main/java/com/backendsys/modules/system/service/SysAuthService.java

@@ -19,4 +19,10 @@ public interface SysAuthService {
     Map<String, Object> register(SysUserDTO sysUserDTO);
     Map<String, Object> forgotPassword(SysUserDTO sysUserDTO);
     Map<String, Object> logout();
+
+    // [方法] 登录失败 (通用) (errMsg: 错误提示文本, username: 用户名, intercept: 是否拦截)
+    void loginFail(String errMsg, String username, Boolean isIntercept);
+    void cleanLoginRequired(String key);
+    // [方法] 登录成功
+    SysUserInfo loginSuccess(Long user_id, Integer is_remember);
 }

+ 5 - 3
src/main/java/com/backendsys/modules/system/service/impl/SysAuthServiceImpl.java

@@ -26,6 +26,7 @@ import jakarta.servlet.ServletOutputStream;
 import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.core.env.Environment;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.stereotype.Service;
@@ -120,12 +121,13 @@ public class SysAuthServiceImpl implements SysAuthService {
     }
 
     @Override
+    @Cacheable(value = "catch::mobile-area", key = "'list'", unless = "#result == null")
     public List<SysMobileArea> getMobileAreaList(SysMobileArea sysMobileArea) {
         return sysMobileAreaDao.selectMobileAreaList(sysMobileArea);
     }
 
     // [方法] 登录失败 (通用) (errMsg: 错误提示文本, username: 用户名, intercept: 是否拦截)
-    private void loginFail(String errMsg, String username, Boolean isIntercept) {
+    public void loginFail(String errMsg, String username, Boolean isIntercept) {
 
         // 验证码是否必填
         Boolean currentCaptchaRequired = captchaUtil.isCaptchaRequired(APPLICATION_NAME + "-login-required-captcha-" + username, 3);
@@ -145,7 +147,7 @@ public class SysAuthServiceImpl implements SysAuthService {
     }
 
     // [方法] 登录成功
-    private SysUserInfo loginSuccess(Long user_id, Integer is_remember) {
+    public SysUserInfo loginSuccess(Long user_id, Integer is_remember) {
 
         // [查询] 登录的用户信息
         SysUserInfo sysUserInfo = sysUserService.selectUserInfo(user_id);
@@ -211,7 +213,7 @@ public class SysAuthServiceImpl implements SysAuthService {
         redisUtil.setCacheObject(APPLICATION_NAME + "-login-required-captcha-" + key, currentErrCount, 1, TimeUnit.MINUTES);
         System.out.println("currentErrCount: " + currentErrCount);
     }
-    private void cleanLoginRequired(String key) {
+    public void cleanLoginRequired(String key) {
         redisUtil.delete(APPLICATION_NAME + "-login-required-captcha-" + key);
     }
 

+ 0 - 7
src/main/java/com/backendsys/modules/system/service/impl/SysUserServiceImpl.java

@@ -144,14 +144,10 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUser> impleme
         // 获得 用户账号信息
         SysUser sysUser = sysUserDao.selectOne(new QueryWrapper<SysUser>().eq("id", user_id));
         if (sysUser == null) throw new CustException("用户不存在");
-        System.out.println("sysUser = " + sysUser);
-        System.out.println("-----------");
 
         // 获得 用户基本信息
         SysUserInfo sysUserInfo = sysUserInfoDao.selectOne(new QueryWrapper<SysUserInfo>().eq("user_id", user_id));
         if (sysUserInfo == null) throw new CustException("用户不存在");
-        System.out.println("sysUserInfo = " + sysUserInfo);
-        System.out.println("-----------");
 
         // 获得 用户角色
         List<Map<String, Object>> roles = sysUserRoleDao.selectRoleByUserId(user_id);
@@ -168,9 +164,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUser> impleme
         // 用户账号信息 赋值到 用户基本信息
         BeanUtils.copyProperties(sysUser, sysUserInfo);
 
-        System.out.println("sysUserInfo + sysUser = " + sysUserInfo);
-        System.out.println("-----------");
-
         // 用户基本信息 赋值到 用户基本信息(简约)
         SysUserInfoSimple sysUserInfoSimple = new SysUserInfoSimple();
         BeanUtils.copyProperties(sysUserInfo, sysUserInfoSimple);

+ 2 - 0
src/main/resources/mapper/system/SysUserDao.xml

@@ -244,6 +244,7 @@
             <if test="gender != null and gender != ''">, gender</if>
             <if test="avatar != null and avatar != ''">, avatar</if>
             <if test="status != null and status != ''">, status</if>
+            <if test="audit_status != null and audit_status != ''">, audit_status</if>
             <if test="invite_code != null and invite_code != ''">, invite_code</if>
         ) VALUES (@last_user_id
             <if test="nickname != null and nickname != ''">, #{nickname}</if>
@@ -251,6 +252,7 @@
             <if test="gender != null and gender != ''">, #{gender}</if>
             <if test="avatar != null and avatar != ''">, #{avatar}</if>
             <if test="status != null and status != ''">, #{status}</if>
+            <if test="audit_status != null and audit_status != ''">, #{audit_status}</if>
             <if test="invite_code != null and invite_code != ''">, #{invite_code}</if>
         );