Browse Source

修复登录时间(utc)

tsurumure 3 weeks ago
parent
commit
e20b0027f2

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

+ 1 - 0
db/sys_user_role_permission.sql

@@ -186,6 +186,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),

+ 4 - 3
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'),
@@ -172,7 +172,7 @@ 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'),
@@ -184,7 +184,7 @@ INSERT INTO sys_user_role_permission_relation(role_id, permission_id) VALUES
     (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'),
 
@@ -193,6 +193,7 @@ INSERT INTO sys_user_role_permission_relation(role_id, permission_id) VALUES
     (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 - 1
src/main/java/com/backendsys/modules/material/controller/MaterialController.java

@@ -29,7 +29,7 @@ public class MaterialController {
         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) {

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

@@ -0,0 +1,45 @@
+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.RestController;
+
+@Validated
+@RestController
+@Tag(name = "素材需求反馈管理")
+public class MaterialFeedbackController {
+
+    @Autowired
+    private MaterialFeedbackService materialFeedbackService;
+
+//    @Anonymous
+//    @Operation(summary = "获取素材需求反馈列表")
+//    @GetMapping("/api/material/getMaterialFeedbackList")
+//    public Result getMaterialFeedbackList(@Validated(MaterialFeedback.FeedbackList.class) MaterialFeedback materialFeedback) {
+//        return Result.success().put("data", materialFeedbackService.selectMaterialFeedbackList(materialFeedback));
+//    }
+//
+//    @Anonymous
+//    @Operation(summary = "获取素材需求反馈列表(下拉)")
+//    @GetMapping("/api/material/getMaterialFeedbackPopover")
+//    public Result getMaterialFeedbackPopover(@Validated(MaterialFeedback.FeedbackList.class) MaterialFeedback materialFeedback) {
+//        return Result.success().put("data", materialFeedbackService.selectMaterialFeedbackPopover(materialFeedback));
+//    }
+
+    @Operation(summary = "发送素材需求反馈")
+    @PostMapping("/api/material/sendMaterialFeedback")
+    public Result sendMaterialFeedback(@Validated(MaterialFeedback.Create.class) MaterialFeedback materialFeedback) {
+        materialFeedback.setUser_id(SecurityUtil.getUserId());
+        return Result.success().put("data", materialFeedbackService.sendMaterialFeedback(materialFeedback));
+    }
+
+}

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

@@ -0,0 +1,9 @@
+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;
+
+@Mapper
+public interface MaterialFeedbackDao extends BaseMapper<MaterialFeedback> {
+}

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

@@ -0,0 +1,49 @@
+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;
+
+    @NotNull(message="素材风格ID不能为空", groups = { Create.class, Update.class })
+    private Long lora_id;
+
+    @NotNull(message="素材分类ID不能为空", groups = { Create.class, Update.class })
+    private Long category_id;
+
+    @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;
+    
+}

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

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

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

@@ -0,0 +1,25 @@
+package com.backendsys.modules.material.service.impl;
+
+import com.backendsys.modules.material.dao.MaterialFeedbackDao;
+import com.backendsys.modules.material.entity.MaterialFeedback;
+import com.backendsys.modules.material.service.MaterialFeedbackService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+@Service
+public class MaterialFeedbackServiceImpl implements MaterialFeedbackService {
+
+    @Autowired
+    private MaterialFeedbackDao materialFeedbackDao;
+
+    /**
+     * 发送素材需求反馈
+     */
+    @Override
+    public Map<String, Object> sendMaterialFeedback(MaterialFeedback materialFeedback) {
+        return null;
+    }
+
+}

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

+ 2 - 1
src/main/java/com/backendsys/modules/system/entity/SysUserInfo.java

@@ -38,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)

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

+ 4 - 1
src/main/java/com/backendsys/modules/system/service/impl/SysAuthServiceImpl.java

@@ -36,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;
 
@@ -175,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);
 
         // [系统配置] 系统用户默认登录过期时间(小时)

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

+ 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" />