浏览代码

Merge branch 'master' of ssh://git.daoguyujia.com:10022/gogs/BackendSys

Mure 3 周之前
父节点
当前提交
06e67fa7f6
共有 42 个文件被更改,包括 698 次插入113 次删除
  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. 27 0
      db/ai_material_feedback.sql
  7. 1 1
      db/ai_material_lora.sql
  8. 14 6
      db/ai_material_tag.sql
  9. 1 1
      db/sys_user.sql
  10. 1 0
      db/sys_user_role_menu.sql
  11. 1 0
      db/sys_user_role_permission.sql
  12. 6 4
      db/sys_user_role_permission_relation.sql
  13. 25 17
      src/main/java/com/backendsys/config/Mybatis/handler/timezone/LocalDateTimeHandler.java
  14. 1 34
      src/main/java/com/backendsys/modules/common/config/security/SecurityConfig.java
  15. 2 1
      src/main/java/com/backendsys/modules/material/controller/MaterialCategoryController.java
  16. 4 2
      src/main/java/com/backendsys/modules/material/controller/MaterialController.java
  17. 38 0
      src/main/java/com/backendsys/modules/material/controller/MaterialFeedbackController.java
  18. 3 2
      src/main/java/com/backendsys/modules/material/controller/MaterialLoraController.java
  19. 2 1
      src/main/java/com/backendsys/modules/material/controller/MaterialTagController.java
  20. 10 0
      src/main/java/com/backendsys/modules/material/controller/MaterialUserController.java
  21. 13 0
      src/main/java/com/backendsys/modules/material/dao/MaterialFeedbackDao.java
  22. 57 0
      src/main/java/com/backendsys/modules/material/entity/MaterialFeedback.java
  23. 28 0
      src/main/java/com/backendsys/modules/material/entity/MaterialUser.java
  24. 16 0
      src/main/java/com/backendsys/modules/material/service/MaterialFeedbackService.java
  25. 4 0
      src/main/java/com/backendsys/modules/material/service/MaterialUserService.java
  26. 1 2
      src/main/java/com/backendsys/modules/material/service/impl/MaterialCategoryServiceImpl.java
  27. 71 0
      src/main/java/com/backendsys/modules/material/service/impl/MaterialFeedbackServiceImpl.java
  28. 22 1
      src/main/java/com/backendsys/modules/material/service/impl/MaterialServiceImpl.java
  29. 1 1
      src/main/java/com/backendsys/modules/material/service/impl/MaterialTagServiceImpl.java
  30. 122 4
      src/main/java/com/backendsys/modules/material/service/impl/MaterialUserServiceImpl.java
  31. 1 1
      src/main/java/com/backendsys/modules/sms/entity/Sms.java
  32. 4 1
      src/main/java/com/backendsys/modules/sms/service/impl/SmsServiceImpl.java
  33. 11 3
      src/main/java/com/backendsys/modules/system/entity/SysUserInfo.java
  34. 5 2
      src/main/java/com/backendsys/modules/system/entity/SysUserInfoSimple.java
  35. 6 0
      src/main/java/com/backendsys/modules/system/service/SysAuthService.java
  36. 3 0
      src/main/java/com/backendsys/modules/system/service/SysUserRoleService.java
  37. 9 4
      src/main/java/com/backendsys/modules/system/service/impl/SysAuthServiceImpl.java
  38. 10 0
      src/main/java/com/backendsys/modules/system/service/impl/SysUserRoleServiceImpl.java
  39. 0 3
      src/main/java/com/backendsys/modules/system/service/impl/SysUserServiceImpl.java
  40. 59 0
      src/main/resources/mapper/ai/material/MaterialFeedbackDao.xml
  41. 2 0
      src/main/resources/mapper/system/SysUserDao.xml
  42. 3 3
      src/main/resources/mapper/system/SysUserInfoDao.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)
 ;
 

+ 27 - 0
db/ai_material_feedback.sql

@@ -0,0 +1,27 @@
+/**
+Source Server Version: 8.0.31
+Source Database: backendsys
+Date: 2025/07/28 15:57:12
+*/
+
+DROP TABLE IF EXISTS `ai_material_feedback`;
+CREATE TABLE `ai_material_feedback` (
+    PRIMARY KEY (`id`),
+    `id` BIGINT AUTO_INCREMENT COMMENT 'ID',
+    `user_id` BIGINT NOT NULL COMMENT '用户ID',
+    `lora_id` BIGINT NOT NULL COMMENT '素材风格ID',
+    `category_id` BIGINT NOT NULL COMMENT '素材分类ID',
+    `content` VARCHAR(2000) NOT NULL COMMENT '需求说明 (500字以内)',
+    `image_url` VARCHAR(2000) COMMENT '参考图',
+    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    INDEX `user_id` (`user_id`),
+    INDEX `idx_lora_id` (`lora_id`),
+    INDEX `category_id` (`category_id`)
+) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='素材需求反馈表';
+
+INSERT INTO ai_material_feedback(user_id, lora_id, category_id, content, image_url) VALUES
+    (1, 2, 2, '这是一段反馈和建议的文字', 'https://www.xxx.com/xx.png'),
+    (1, 2, 2, '这是一段反馈和建议的文字 (无图)', '')
+;
+

+ 1 - 1
db/ai_material_lora.sql

@@ -14,7 +14,7 @@ CREATE TABLE `ai_material_lora` (
 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='素材风格表';
 
 INSERT INTO ai_material_lora(lora_name, sort) VALUES
-    ('沙雕素材', 2),
+    ('精品主角团', 2),
     ('简笔画素材', 1)
 ;
 

+ 14 - 6
db/ai_material_tag.sql

@@ -23,24 +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-沙雕素材-异兽素材
+    (3, '8090场景', 7),
+    (3, '末日悬疑场景', 6),
+    # 4-沙雕素材-异兽素材
     (4, '异兽', 10),
     (4, '动物', 9),
-    # 4-沙雕素材-道具素材
+    # 5-沙雕素材-道具素材
     (5, '修仙道具', 10),
     (5, '都市道具', 9),
     (5, '历史道具', 8),
 
-
     # 6-简笔画素材-人物素材
     (6, '精品人物', 10),
     (6, '修仙人物', 9),
@@ -59,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`),

+ 1 - 0
db/sys_user_role_menu.sql

@@ -47,6 +47,7 @@ INSERT INTO sys_user_role_menu(id, parent_id, menu_name, menu_name_en, type, pat
     (20, -1, '素材管理', 'Material', 1, '/material', '', '{}', 'MessageBox', '20', 13),
     (21, 20, '素材中心', 'Material Center', 1, '/material/materialList', '/src/views/material/materialList.vue', '{}', null, '20.1', 13),
     (22, 20, '素材用户', 'Material User', 1, '/material/materialUserList', '/src/views/material/materialUserList.vue', '{}', null, '20.4', 12),
+    (23, 20, '素材需求反馈', 'Material Feedback', 1, '/material/materialFeedbackList', '/src/views/material/materialFeedbackList.vue', '{}', null, '20.4', 11),
 #     (21, 20, '素材列表', 'Material List', 1, '/material/materialList', '/src/views/material/materialList.vue', '{}', null, '20.1', 13),
 #     (21, 20, '沙雕素材', 'Material Sa Diao', 1, '/material/materialList?lora_id=1', '/src/views/material/materialList.vue', '{}', null, '20.1', 13),
 #     (22, 20, '简笔画素材', 'Material Simple', 1, '/material/materialList?lora_id=2', '/src/views/material/materialList.vue', '{}', null, '20.1', 13),

+ 1 - 0
db/sys_user_role_permission.sql

@@ -197,6 +197,7 @@ INSERT INTO sys_user_role_permission(id, parent_id, permission_name, sort) VALUE
             ('20.1.2', '20.1', '创建素材', null),
             ('20.1.3', '20.1', '编辑素材', null),
             ('20.1.4', '20.1', '删除素材', null),
+            ('20.1.5', '20.1', '下载素材', null),
         ('20.2', '20', '素材分类列表', null),
         ('20.3', '20', '素材标签列表', null),
         ('20.4', '20', '素材用户列表', null),

+ 6 - 4
db/sys_user_role_permission_relation.sql

@@ -78,7 +78,7 @@ INSERT INTO sys_user_role_permission_relation(role_id, permission_id) VALUES
 
     (1, '20'),
         (1, '20.1'),
-            (1, '20.1.1'), (1, '20.1.2'), (1, '20.1.3'), (1, '20.1.4'),
+            (1, '20.1.1'), (1, '20.1.2'), (1, '20.1.3'), (1, '20.1.4'), (1, '20.1.5'),
         (1, '20.2'),
         (1, '20.3'),
         (1, '20.4'),
@@ -179,27 +179,29 @@ INSERT INTO sys_user_role_permission_relation(role_id, permission_id) VALUES
     (4, '3.2.3.3'), (4, '3.5.1'),
     (4, '20'),
         (4, '20.1'),
-            (4, '20.1.1'), (4, '20.1.2'), (4, '20.1.3'), (4, '20.1.4'),
+            (4, '20.1.1'), (4, '20.1.2'), (4, '20.1.3'), (4, '20.1.4'), (4, '20.1.5'),
         (4, '20.2'),
         (4, '20.3'),
         (4, '20.4'),
             (4, '20.4.1'),
 
     # 运营-素材使用者
-    (5, '1.1.2'), (5, '1.1.6'),
+    (5, '1.1.2'), (5, '1.1.3'), (5, '1.1.6'),
 #     (5, '3.2.1'),
     (5, '3.2.3.3'), (5, '3.5.1'),
     (5, '20'),
         (5, '20.1'),
-            (5, '20.1.1'),
+            (5, '20.1.1'), (5, '20.1.5'),
         (5, '20.2'),
         (5, '20.3'),
 
     # 运营-素材游客
 #     (6, '3.2.1'),
+    (6, '1.1.3'),
     (6, '3.2.3.3'), (6, '3.5.1'),
     (6, '20'),
         (6, '20.1'),
+            (6, '20.1.1'),
     (6, '20.2'),
     (6, '20.3')
 ;

+ 25 - 17
src/main/java/com/backendsys/config/Mybatis/handler/timezone/LocalDateTimeHandler.java

@@ -9,49 +9,57 @@ import org.springframework.stereotype.Component;
 import java.sql.*;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 
 @MappedTypes(LocalDateTime.class)
-@MappedJdbcTypes(JdbcType.TIMESTAMP)
+@MappedJdbcTypes(JdbcType.VARCHAR)   // 关键:对应数据库 VARCHAR
 @Component
 public class LocalDateTimeHandler extends BaseTypeHandler<LocalDateTime> {
 
+    private static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    /* ====== 写入:LocalDateTime -> VARCHAR ====== */
     @Override
-    public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) throws SQLException {
-        ps.setTimestamp(i, java.sql.Timestamp.valueOf(parameter));
+    public void setNonNullParameter(PreparedStatement ps, int i,
+                                    LocalDateTime parameter, JdbcType jdbcType) throws SQLException {
+        ps.setString(i, parameter.format(FMT));
     }
 
+    /* 允许 null */
     @Override
-    public void setParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) throws SQLException {
+    public void setParameter(PreparedStatement ps, int i,
+                             LocalDateTime parameter, JdbcType jdbcType) throws SQLException {
         if (parameter == null) {
-            // 允许数据库使用默认值
-            ps.setNull(i, Types.TIMESTAMP);
+            ps.setNull(i, Types.VARCHAR);
         } else {
             super.setParameter(ps, i, parameter, jdbcType);
         }
     }
 
+    /* ====== 读取:VARCHAR -> LocalDateTime ====== */
     @Override
     public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
-        java.sql.Timestamp timestamp = rs.getTimestamp(columnName);
-        return convertToLocalDateTime(timestamp);
+        return strToLocalDateTime(rs.getString(columnName));
     }
 
     @Override
     public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
-        java.sql.Timestamp timestamp = rs.getTimestamp(columnIndex);
-        return convertToLocalDateTime(timestamp);
+        return strToLocalDateTime(rs.getString(columnIndex));
     }
 
     @Override
     public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
-        java.sql.Timestamp timestamp = cs.getTimestamp(columnIndex);
-        return convertToLocalDateTime(timestamp);
+        return strToLocalDateTime(cs.getString(columnIndex));
     }
 
-    private LocalDateTime convertToLocalDateTime(java.sql.Timestamp timestamp) {
-        if (timestamp == null) return null;
-        return timestamp.toInstant().atZone(ZoneId.of("UTC"))
-            .withZoneSameInstant(ZoneId.of(TimezoneContextHolder.getTimeZone()))
-            .toLocalDateTime();
+    private LocalDateTime strToLocalDateTime(String val) {
+        if (val == null || val.trim().isEmpty()) {
+            return null;
+        }
+        // 按自己的格式解析
+        return LocalDateTime.parse(val.trim(), FMT)
+                .atZone(ZoneId.of("UTC"))
+                .withZoneSameInstant(ZoneId.of(TimezoneContextHolder.getTimeZone()))
+                .toLocalDateTime();
     }
 }

+ 1 - 34
src/main/java/com/backendsys/modules/common/config/security/SecurityConfig.java

@@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpHeaders;
 import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -107,40 +108,6 @@ public class SecurityConfig {
             .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
             ;
 
-            // 使用无状态session,即不使用session缓存数据
-//            .logout()
-//            .logoutUrl("/api/auth/logout")
-//            .addLogoutHandler(new CustomLogoutHandler())
-//            .logoutSuccessHandler(((request, response, authentication) -> SecurityContextHolder.clearContext()));
-
-//            .authorizeHttpRequests(
-//                (requests) -> requests
-//                    // 放行路径 (6.0 下,antMatchers 已弃用,使用 requestMatchers 代替)
-//                    .requestMatchers("/", "/api/**").permitAll()
-//                    // 放行静态资源 (/resources/static/images/p1.jpg)
-//                    .requestMatchers("/*.ico", "/images/**").permitAll()
-//                    // ,
-//                .anyRequest().authenticated()
-//            )
-//            .formLogin((form) ->
-//                form
-//                    // 进入登录跳转页面
-//                    .loginPage("/login")
-//
-//                    // 自定义拦截器 (错误/成功)
-//                    .failureHandler(new CustomAuthenticationFailureHandler())
-//                    .successHandler(new CustomAuthenticationSuccessHandler())
-//
-//                    .permitAll()
-//                ).logout((logout) ->
-//                    logout
-//                        // 退出登录后跳转页面
-//                        .logoutSuccessUrl("/")
-//                        .permitAll()
-//                );
-
-        // http.cors(); // 允许跨域
-
         return http.build();
     }
 

+ 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));

+ 4 - 2
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,14 +21,15 @@ 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));
     }
 
-    @Operation(summary = "获取素材列表")
+    @Operation(summary = "获取素材详情")
     @PreAuthorize("@sr.hasPermission('20.1.1')")
     @GetMapping("/api/material/getMaterialDetail")
     public Result getMaterialDetail(@Validated(Material.Detail.class) Material material) {

+ 38 - 0
src/main/java/com/backendsys/modules/material/controller/MaterialFeedbackController.java

@@ -0,0 +1,38 @@
+package com.backendsys.modules.material.controller;
+
+import com.backendsys.modules.common.config.security.annotations.Anonymous;
+import com.backendsys.modules.common.config.security.utils.SecurityUtil;
+import com.backendsys.modules.common.utils.Result;
+import com.backendsys.modules.material.entity.MaterialFeedback;
+import com.backendsys.modules.material.service.MaterialFeedbackService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@Validated
+@RestController
+@Tag(name = "素材需求反馈管理")
+public class MaterialFeedbackController {
+
+    @Autowired
+    private MaterialFeedbackService materialFeedbackService;
+
+    @Operation(summary = "获取素材需求反馈列表")
+    @GetMapping("/api/material/getMaterialFeedbackList")
+    public Result getMaterialFeedbackList(MaterialFeedback materialFeedback) {
+        return Result.success().put("data", materialFeedbackService.selectMaterialFeedbackList(materialFeedback));
+    }
+
+    @Operation(summary = "发送素材需求反馈")
+    @PostMapping("/api/material/sendMaterialFeedback")
+    public Result sendMaterialFeedback(@Validated(MaterialFeedback.Create.class) @RequestBody MaterialFeedback materialFeedback) {
+        materialFeedback.setUser_id(SecurityUtil.getUserId());
+        return Result.success().put("data", materialFeedbackService.sendMaterialFeedback(materialFeedback));
+    }
+
+}

+ 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));
+    }
+
 
 }

+ 13 - 0
src/main/java/com/backendsys/modules/material/dao/MaterialFeedbackDao.java

@@ -0,0 +1,13 @@
+package com.backendsys.modules.material.dao;
+
+import com.backendsys.modules.material.entity.MaterialFeedback;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+import java.util.Map;
+
+@Mapper
+public interface MaterialFeedbackDao extends BaseMapper<MaterialFeedback> {
+    List<Map<String, Object>> selectMaterialFeedbackList(MaterialFeedback materialFeedback);
+}

+ 57 - 0
src/main/java/com/backendsys/modules/material/entity/MaterialFeedback.java

@@ -0,0 +1,57 @@
+package com.backendsys.modules.material.entity;
+
+import com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeAdapter;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.google.gson.annotations.JsonAdapter;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("ai_material_feedback")
+public class MaterialFeedback {
+
+    public static interface Create{}
+    public static interface Update{}
+    public static interface Delete{}
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    @TableField("id")
+    private Long feedback_id;
+
+    private Long user_id;
+    @TableField(exist = false)
+    private String phone;
+
+    @NotNull(message="素材风格ID不能为空", groups = { Create.class, Update.class })
+    private Long lora_id;
+
+    @TableField(exist = false)
+    private String lora_name;
+
+    @NotNull(message="素材分类ID不能为空", groups = { Create.class, Update.class })
+    private Long category_id;
+
+    @TableField(exist = false)
+    private String category_name;
+
+    @Size(max = 500, message = "需求说明长度不超过 {max} 个字符", groups = { Create.class, Update.class })
+    @NotEmpty(message="需求说明不能为空", groups = { Create.class, Update.class })
+    private String content;
+    private String image_url;
+
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime create_time;
+
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime update_time;
+    
+}

+ 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;
+    
+    
+}

+ 16 - 0
src/main/java/com/backendsys/modules/material/service/MaterialFeedbackService.java

@@ -0,0 +1,16 @@
+package com.backendsys.modules.material.service;
+
+import com.backendsys.modules.material.entity.MaterialFeedback;
+import com.backendsys.utils.response.PageEntity;
+
+import java.util.Map;
+
+public interface MaterialFeedbackService {
+
+    // 获取素材需求反馈列表
+    PageEntity selectMaterialFeedbackList(MaterialFeedback materialFeedback);
+
+    // 发送素材需求反馈
+    Map<String, Object> sendMaterialFeedback(MaterialFeedback materialFeedback);
+
+}

+ 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);
+
 }

+ 1 - 2
src/main/java/com/backendsys/modules/material/service/impl/MaterialCategoryImpl.java → src/main/java/com/backendsys/modules/material/service/impl/MaterialCategoryServiceImpl.java

@@ -6,14 +6,13 @@ import com.backendsys.modules.material.service.MaterialCategoryService;
 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.stereotype.Service;
 
 import java.util.List;
 
 @Service
-public class MaterialCategoryImpl implements MaterialCategoryService {
+public class MaterialCategoryServiceImpl implements MaterialCategoryService {
 
     @Autowired
     private MaterialCategoryDao materialCategoryDao;

+ 71 - 0
src/main/java/com/backendsys/modules/material/service/impl/MaterialFeedbackServiceImpl.java

@@ -0,0 +1,71 @@
+package com.backendsys.modules.material.service.impl;
+
+import com.backendsys.exception.CustException;
+import com.backendsys.modules.material.dao.MaterialCategoryDao;
+import com.backendsys.modules.material.dao.MaterialFeedbackDao;
+import com.backendsys.modules.material.dao.MaterialLoraDao;
+import com.backendsys.modules.material.entity.MaterialCategory;
+import com.backendsys.modules.material.entity.MaterialFeedback;
+import com.backendsys.modules.material.entity.MaterialLora;
+import com.backendsys.modules.material.service.MaterialFeedbackService;
+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.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class MaterialFeedbackServiceImpl implements MaterialFeedbackService {
+
+    @Autowired
+    private MaterialLoraDao materialLoraDao;
+    @Autowired
+    private MaterialCategoryDao materialCategoryDao;
+    @Autowired
+    private MaterialFeedbackDao materialFeedbackDao;
+
+
+    /**
+     * 获取素材需求反馈列表
+     */
+    @Override
+    public PageEntity selectMaterialFeedbackList(MaterialFeedback materialFeedback) {
+        PageUtils.startPage();  // 分页
+        List<Map<String, Object>> list = materialFeedbackDao.selectMaterialFeedbackList(materialFeedback);
+        return new PageInfoResult(list).toEntity();
+    }
+
+
+    /**
+     * 发送素材需求反馈
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> sendMaterialFeedback(MaterialFeedback materialFeedback) {
+
+        // [DB] 判断素材风格是否存在
+        Long lora_id = materialFeedback.getLora_id();
+        LambdaQueryWrapper<MaterialLora> wrapperLora = new LambdaQueryWrapper<>();
+        wrapperLora.eq(MaterialLora::getLora_id, lora_id);
+        Boolean is_lora_exist = materialLoraDao.exists(wrapperLora);
+        if (!is_lora_exist) throw new CustException("素材风格不存在");
+
+        // [DB] 判断素材分类是否存在
+        Long category_id = materialFeedback.getCategory_id();
+        LambdaQueryWrapper<MaterialCategory> wrapperCategory = new LambdaQueryWrapper<>();
+        wrapperCategory.eq(MaterialCategory::getCategory_id, category_id);
+        Boolean is_category_exist = materialCategoryDao.exists(wrapperCategory);
+        if (!is_category_exist) throw new CustException("素材分类不存在");
+
+        // [DB] 新增 素材需求反馈
+        materialFeedbackDao.insert(materialFeedback);
+
+        return Map.of("feedback_id", materialFeedback.getId());
+    }
+
+}

+ 22 - 1
src/main/java/com/backendsys/modules/material/service/impl/MaterialServiceImpl.java

@@ -3,6 +3,7 @@ package com.backendsys.modules.material.service.impl;
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.StrUtil;
 import com.backendsys.exception.CustException;
+import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.material.dao.MaterialCategoryDao;
 import com.backendsys.modules.material.dao.MaterialDao;
 import com.backendsys.modules.material.dao.MaterialTagDao;
@@ -10,6 +11,8 @@ import com.backendsys.modules.material.entity.Material;
 import com.backendsys.modules.material.entity.MaterialCategory;
 import com.backendsys.modules.material.entity.MaterialTag;
 import com.backendsys.modules.material.service.MaterialService;
+import com.backendsys.modules.system.entity.SysUserRole;
+import com.backendsys.modules.system.service.SysUserRoleService;
 import com.backendsys.modules.upload.service.SysFileService;
 import com.backendsys.modules.upload.utils.ObjectKey.ObjectKeyEntity;
 import com.backendsys.modules.upload.utils.ObjectKey.ObjectKeyUtil;
@@ -26,6 +29,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
 @Service
@@ -42,6 +46,8 @@ public class MaterialServiceImpl implements MaterialService {
     private MaterialCategoryDao materialCategoryDao;
     @Autowired
     private SysFileService sysFileService;
+    @Autowired
+    private SysUserRoleService sysUserRoleService;
 
     private List<MaterialTag> getMaterialTagByIds(String tag_ids) {
         if (StrUtil.isEmpty(tag_ids)) return null;
@@ -90,10 +96,25 @@ public class MaterialServiceImpl implements MaterialService {
         Material detail = materialDao.selectMaterialDetail(material);
         if (detail == null) throw new CustException("素材不存在");
 
-        // 新增字段:标签列表
+        // [新增字段] 标签列表
         List<MaterialTag> materialTagList = getMaterialTagByIds(detail.getTag_ids());
         detail.setTag_list(materialTagList);
 
+        // [DB] 查询当前用户角色,如果是素材游客,则不显示图片
+        List<SysUserRole> role_list = sysUserRoleService.selectUserRoleByUserId(SecurityUtil.getUserId());
+        AtomicReference<Boolean> hasPermission = new AtomicReference<>(false);
+        role_list.stream().forEach(role -> {
+            if ("MATERIAL_USER".equals(role.getRole_sign()) || "MATERIAL_ADMIN".equals(role.getRole_sign())) {
+                hasPermission.set(true);
+            }
+        });
+
+        // 如果当前用户没有权限,则不显示图片
+        if (!hasPermission.get()) {
+            detail.setFile_url("");
+            detail.setPan_baidu_url("");
+        }
+
         return detail;
     }
 

+ 1 - 1
src/main/java/com/backendsys/modules/material/service/impl/MaterialTagImpl.java → src/main/java/com/backendsys/modules/material/service/impl/MaterialTagServiceImpl.java

@@ -15,7 +15,7 @@ import org.springframework.stereotype.Service;
 import java.util.List;
 
 @Service
-public class MaterialTagImpl implements MaterialTagService {
+public class MaterialTagServiceImpl implements MaterialTagService {
 
     @Autowired
     private MaterialDao materialDao;

+ 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);
 
         // [插入] 新增短信记录

+ 11 - 3
src/main/java/com/backendsys/modules/system/entity/SysUserInfo.java

@@ -1,15 +1,18 @@
 package com.backendsys.modules.system.entity;
 
+import com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeAdapter;
 import com.backendsys.entity.System.SysUserDTO;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.google.gson.annotations.JsonAdapter;
 import jakarta.validation.constraints.NotEmpty;
 import lombok.Data;
 
 import jakarta.validation.constraints.NotNull;
 
+import java.time.LocalDateTime;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -35,7 +38,8 @@ public class SysUserInfo {
     private String avatar;
     private String last_login_ip;
     private String last_login_uuid;
-    private String last_login_time;
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime last_login_time;
     private Integer is_super;
 
     @TableField(exist = false)
@@ -46,8 +50,12 @@ public class SysUserInfo {
     private Integer status;
     private Integer audit_status;
     private String audit_note;
-    private String create_time;
-    private String update_time;
+
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime create_time;
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime update_time;
+
     private Integer del_flag;
 
     @TableField(exist = false)

+ 5 - 2
src/main/java/com/backendsys/modules/system/entity/SysUserInfoSimple.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Map;
 
@@ -33,8 +34,10 @@ public class SysUserInfoSimple {
     private Integer status;
     private Integer audit_status;
     private String audit_note;
-    private String create_time;
-    private String update_time;
+
+    private LocalDateTime create_time;
+    private LocalDateTime update_time;
+
     private Integer del_flag;
     private List<Map<String, Object>> roles;
     private List<Long> role_id;

+ 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);
 }

+ 3 - 0
src/main/java/com/backendsys/modules/system/service/SysUserRoleService.java

@@ -4,6 +4,7 @@ import com.backendsys.modules.system.entity.SysUserRole;
 import com.backendsys.utils.response.PageEntity;
 import com.baomidou.mybatisplus.extension.service.IService;
 
+import java.util.List;
 import java.util.Map;
 
 public interface SysUserRoleService extends IService<SysUserRole> {
@@ -14,4 +15,6 @@ public interface SysUserRoleService extends IService<SysUserRole> {
     Map<String, Object> updateUserRole(SysUserRole sysUserRole);
     Map<String, Object> deleteUserRole(SysUserRole sysUserRole);
 
+    // 根据用户ID获取用户角色集合
+    List<SysUserRole> selectUserRoleByUserId(Long user_id);
 }

+ 9 - 4
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;
@@ -35,6 +36,8 @@ import javax.imageio.ImageIO;
 import java.awt.image.BufferedImage;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 
@@ -120,12 +123,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 +149,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);
@@ -173,7 +177,8 @@ public class SysAuthServiceImpl implements SysAuthService {
         String uuid = Convert.toStr(UUID.randomUUID());
         sysUserInfo.setLast_login_uuid(uuid);
         sysUserInfo.setLast_login_ip(httpRequestUtil.getIpAddr());
-        sysUserInfo.setLast_login_time(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
+//        sysUserInfo.setLast_login_time(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
+        sysUserInfo.setLast_login_time(LocalDateTime.now(ZoneOffset.UTC));
         sysUserInfoDao.updateById(sysUserInfo);
 
         // [系统配置] 系统用户默认登录过期时间(小时)
@@ -211,7 +216,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);
     }
 

+ 10 - 0
src/main/java/com/backendsys/modules/system/service/impl/SysUserRoleServiceImpl.java

@@ -128,4 +128,14 @@ public class SysUserRoleServiceImpl extends ServiceImpl<SysUserRoleDao, SysUserR
         return Map.of("role_id", sysUserRole.getRole_id());
     }
 
+    /**
+     * 根据用户ID获取用户角色集合
+     */
+    @Override
+    public List<SysUserRole> selectUserRoleByUserId(Long user_id) {
+        List<Long> role_ids = sysUserRoleRelationDao.selectUserRoleIds(user_id);
+        LambdaQueryWrapper<SysUserRole> wrapper = new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getRole_id, role_ids);
+        return sysUserRoleDao.selectList(wrapper);
+    }
+
 }

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

@@ -157,13 +157,10 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUser> impleme
         List<Long> role_ids = roles.stream().map(m -> Convert.toLong(m.get("role_id"))).collect(Collectors.toList());
         sysUserInfo.setRole_id(role_ids);
 
-
-
         // 获得用户积分 (需要权限)
         Integer integral = sysUserIntegralService.selectIntegralByUserId(user_id);
         sysUserInfo.setIntegral(integral);
 
-
         // 用户账号信息 赋值到 用户基本信息
         BeanUtils.copyProperties(sysUser, sysUserInfo);
 

+ 59 - 0
src/main/resources/mapper/ai/material/MaterialFeedbackDao.xml

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.backendsys.modules.material.dao.MaterialFeedbackDao">
+
+    <sql id="includeMaterialFeedback">
+        amf.id,
+        amf.id feedback_id,
+        amf.user_id,
+        u.phone,
+        amf.lora_id,
+        aml.lora_name,
+        amf.category_id,
+        amc.category_name,
+        amf.content,
+        COALESCE(amf.image_url, '') image_url,
+        amf.create_time,
+        amf.update_time
+    </sql>
+    <!-- COALESCE(content_type, '') content_type, -->
+    <resultMap id="resultMapMaterialFeedback" type="com.backendsys.modules.material.entity.MaterialFeedback">
+        <id property="id" column="id" jdbcType="BIGINT" />
+        <result property="feedback_id" column="id" javaType="java.lang.Long" />
+        <result property="user_id" column="user_id" javaType="java.lang.Long" />
+        <result property="phone" column="phone" />
+        <result property="lora_id" column="lora_id" javaType="java.lang.Long" />
+        <result property="lora_name" column="lora_name" />
+        <result property="category_id" column="category_id" javaType="java.lang.Long" />
+        <result property="category_name" column="category_name" />
+        <result property="content" column="content" />
+        <result property="image_url" column="image_url" />
+        <result property="create_time" column="create_time"
+                typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
+        <result property="update_time" column="update_time"
+                typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
+    </resultMap>
+
+    <select id="selectMaterialFeedbackList" resultMap="resultMapMaterialFeedback">
+        SELECT
+            <include refid="includeMaterialFeedback" />
+        FROM ai_material_feedback amf
+        LEFT JOIN ai_material_category amc ON amf.category_id = amc.id
+        LEFT JOIN ai_material_lora aml ON amf.lora_id = aml.id
+        LEFT JOIN sys_user u ON amf.user_id = u.id
+        <where>
+            <if test="user_id != null and user_id != ''">
+                AND amf.user_id = #{user_id}
+            </if>
+            <if test="lora_id != null and lora_id != ''">
+                AND amf.lora_id = #{lora_id}
+            </if>
+            <if test="category_id != null and category_id != ''">
+                AND amf.category_id = #{category_id}
+            </if>
+        </where>
+        ORDER BY amf.create_time DESC
+    </select>
+
+
+</mapper>

+ 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>
         );
 

+ 3 - 3
src/main/resources/mapper/system/SysUserInfoDao.xml

@@ -38,7 +38,7 @@
         <result property="avatar" column="avatar" />
         <result property="last_login_ip" column="last_login_ip" />
         <result property="last_login_uuid" column="last_login_uuid" />
-        <result property="last_login_time" column="last_login_time" />
+        <result property="last_login_time" column="last_login_time" typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
         <result property="is_super" column="is_super" javaType="java.lang.Integer" />
         <result property="integral" column="integral" javaType="java.lang.Integer" />
         <result property="point_balance" column="point_balance" javaType="java.lang.Float" />
@@ -46,8 +46,8 @@
         <result property="status" column="status" javaType="java.lang.Integer"/>
         <result property="audit_status" column="audit_status" javaType="java.lang.Integer" />
         <result property="audit_note" column="audit_note" />
-        <result property="create_time" column="create_time" />
-        <result property="update_time" column="update_time" />
+        <result property="create_time" column="create_time" typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
+        <result property="update_time" column="update_time" typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
         <result property="del_flag" column="del_flag" javaType="java.lang.Integer" />
         <result property="role_ids" column="role_ids" javaType="java.util.List" jdbcType="VARCHAR"
             typeHandler="com.backendsys.config.Mybatis.handler.StringToListTypeHandler" />