Quellcode durchsuchen

提交新增铲车司机app修改密码密码

chenxiaofei vor 14 Stunden
Ursprung
Commit
e3423a3b0c
24 geänderte Dateien mit 841 neuen und 109 gelöschten Zeilen
  1. 67 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/controller/forkliftapp/FeedbacksController.java
  2. 22 2
      sckw-modules/sckw-system/src/main/java/com/sckw/system/controller/forkliftapp/UsersController.java
  3. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/AppVersionsDao.java
  4. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/FeedbacksDao.java
  5. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/LoadingRecordsDao.java
  6. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/LoginLogsDao.java
  7. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/MaterialsDao.java
  8. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/SysArticlesDao.java
  9. 44 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/FeedbackListReqVo.java
  10. 45 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/LoginReqVo.java
  11. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/LogoutReqVo.java
  12. 53 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/SubmitFeedbackReqVo.java
  13. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/SwitchAccountReqVo.java
  14. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/UserInfoReqVo.java
  15. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/AppVersionsResVo.java
  16. 86 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/FeedbackResVo.java
  17. 70 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/LoginResVo.java
  18. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/SwitchAccountResVo.java
  19. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/SysArticlesResVo.java
  20. 3 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/UserInfoResVo.java
  21. 0 88
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/UsersResVo.java
  22. 154 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsFeedbackService.java
  23. 261 3
      sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsUserService.java
  24. 0 16
      sql/2026/01/2026_01_07_chenxiaofei_create.sql

+ 67 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/controller/forkliftapp/FeedbacksController.java

@@ -0,0 +1,67 @@
+package com.sckw.system.controller.forkliftapp;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.sckw.core.web.response.BaseResult;
+import com.sckw.system.model.vo.req.FeedbackListReqVo;
+import com.sckw.system.model.vo.req.SubmitFeedbackReqVo;
+import com.sckw.system.model.vo.res.FeedbackResVo;
+import com.sckw.system.service.KwsFeedbackService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 意见反馈Controller
+ * @author chenxiaofei
+ * @date 2026-01-12
+ */
+@RestController
+@RequestMapping("/feedback")
+@Tag(name = "意见反馈相关接口")
+@RequiredArgsConstructor
+public class FeedbacksController {
+
+    private final KwsFeedbackService kwsFeedbackService;
+
+    /**
+     * 提交意见反馈
+     * @param reqVo 提交反馈请求对象
+     * @return BaseResult<FeedbackResVo>
+     */
+    @PostMapping("/submit")
+    @Operation(summary = "提交意见反馈", description = "用户提交意见反馈,可上传图片")
+    public BaseResult<FeedbackResVo> submitFeedback(@RequestBody @Valid SubmitFeedbackReqVo reqVo) {
+        FeedbackResVo resVo = kwsFeedbackService.submitFeedback(reqVo);
+        return BaseResult.success(resVo);
+    }
+
+    /**
+     * 查询用户反馈列表(分页)
+     * @param reqVo 反馈列表查询请求对象
+     * @return BaseResult<Page<FeedbackResVo>>
+     */
+    @PostMapping("/list")
+    @Operation(summary = "查询反馈列表", description = "分页查询当前用户的反馈列表")
+    public BaseResult<Page<FeedbackResVo>> getFeedbackList(@RequestBody @Valid FeedbackListReqVo reqVo) {
+        Page<FeedbackResVo> page = kwsFeedbackService.getFeedbackList(reqVo);
+        return BaseResult.success(page);
+    }
+
+    /**
+     * 查询反馈详情
+     * @param feedbackId 反馈ID
+     * @return BaseResult<FeedbackResVo>
+     */
+    @GetMapping("/detail/{feedbackId}")
+    @Operation(summary = "查询反馈详情", description = "根据反馈ID查询详细信息")
+    public BaseResult<FeedbackResVo> getFeedbackDetail(@PathVariable("feedbackId") Long feedbackId) {
+        FeedbackResVo resVo = kwsFeedbackService.getFeedbackDetail(feedbackId);
+        return BaseResult.success(resVo);
+    }
+
+}
+
+
+

+ 22 - 2
sckw-modules/sckw-system/src/main/java/com/sckw/system/controller/forkliftapp/UsersController.java

@@ -1,15 +1,18 @@
 package com.sckw.system.controller.forkliftapp;
 
 import com.sckw.core.web.response.BaseResult;
+import com.sckw.system.model.vo.req.LoginReqVo;
 import com.sckw.system.model.vo.req.LogoutReqVo;
 import com.sckw.system.model.vo.req.SwitchAccountReqVo;
 import com.sckw.system.model.vo.req.UpdatePasswordReqVo;
 import com.sckw.system.model.vo.req.UserInfoReqVo;
+import com.sckw.system.model.vo.res.LoginResVo;
 import com.sckw.system.model.vo.res.SwitchAccountResVo;
 import com.sckw.system.model.vo.res.UserInfoResVo;
 import com.sckw.system.service.KwsUserService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
 import jakarta.validation.Valid;
 import lombok.RequiredArgsConstructor;
 import org.springframework.web.bind.annotation.*;
@@ -27,6 +30,22 @@ public class UsersController {
 
     private final KwsUserService kwsUserService;
 
+    /**
+     * 用户登录
+     * @param reqVo 登录请求对象
+     * @param request HTTP请求
+     * @return BaseResult<LoginResVo>
+     */
+    @PostMapping("/login")
+    @Operation(summary = "用户登录", description = "用户登录,返回用户信息和角色")
+    public BaseResult<LoginResVo> login(@RequestBody @Valid LoginReqVo reqVo, HttpServletRequest request) {
+        return BaseResult.success(kwsUserService.login(
+                reqVo.getAccount(),
+                reqVo.getPassword(),
+                reqVo.getSystemType(),
+                request));
+    }
+
     /**
      * 根据用户ID查询用户信息
      * @param reqVo 用户信息查询请求对象
@@ -57,11 +76,12 @@ public class UsersController {
      */
     @PostMapping("/switchAccount")
     @Operation(summary = "切换账号", description = "切换到其他用户账号")
-    public BaseResult<SwitchAccountResVo> switchAccount(@RequestBody @Valid SwitchAccountReqVo reqVo) {
+    public BaseResult<SwitchAccountResVo> switchAccount(@RequestBody @Valid SwitchAccountReqVo reqVo, HttpServletRequest request) {
         return BaseResult.success(kwsUserService.switchAccount(
                 reqVo.getAccount(),
                 reqVo.getPassword(),
-                reqVo.getSystemType()));
+                reqVo.getSystemType(),
+                request));
     }
 
     /**

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/AppVersionsDao.java

@@ -15,3 +15,6 @@ public interface AppVersionsDao extends BaseMapper<AppVersions> {
 }
 
 
+
+
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/FeedbacksDao.java

@@ -15,3 +15,6 @@ public interface FeedbacksDao extends BaseMapper<Feedbacks> {
 }
 
 
+
+
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/LoadingRecordsDao.java

@@ -15,3 +15,6 @@ public interface LoadingRecordsDao extends BaseMapper<LoadingRecords> {
 }
 
 
+
+
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/LoginLogsDao.java

@@ -15,3 +15,6 @@ public interface LoginLogsDao extends BaseMapper<LoginLogs> {
 }
 
 
+
+
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/MaterialsDao.java

@@ -15,3 +15,6 @@ public interface MaterialsDao extends BaseMapper<Materials> {
 }
 
 
+
+
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/SysArticlesDao.java

@@ -15,3 +15,6 @@ public interface SysArticlesDao extends BaseMapper<SysArticles> {
 }
 
 
+
+
+

+ 44 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/FeedbackListReqVo.java

@@ -0,0 +1,44 @@
+package com.sckw.system.model.vo.req;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 反馈列表查询请求对象
+ * @author chenxiaofei
+ * @date 2026-01-12
+ */
+@Data
+@Schema(description = "反馈列表查询请求对象")
+public class FeedbackListReqVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    @NotNull(message = "用户ID不能为空")
+    @Schema(description = "用户ID", required = true, example = "1234567890")
+    private Long userId;
+
+    /**
+     * 当前页码
+     */
+    @Schema(description = "当前页码", example = "1")
+    private Integer pageNum = 1;
+
+    /**
+     * 每页数量
+     */
+    @Schema(description = "每页数量", example = "10")
+    private Integer pageSize = 10;
+
+}
+
+
+

+ 45 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/LoginReqVo.java

@@ -0,0 +1,45 @@
+package com.sckw.system.model.vo.req;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 登录请求对象
+ * @author chenxiaofei
+ * @date 2026-01-07
+ */
+@Data
+@Schema(description = "登录请求对象")
+public class LoginReqVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 账号(手机号)
+     */
+    @NotBlank(message = "账号不能为空")
+    @Schema(description = "账号(手机号)", required = true, example = "13800138000")
+    private String account;
+
+    /**
+     * 密码
+     */
+    @NotBlank(message = "密码不能为空")
+    @Schema(description = "密码", required = true, example = "123456")
+    private String password;
+
+    /**
+     * 系统类型: 1-运营端, 2-企业端
+     */
+    @NotNull(message = "系统类型不能为空")
+    @Schema(description = "系统类型: 1-运营端, 2-企业端", required = true, example = "2")
+    private Integer systemType;
+
+}
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/LogoutReqVo.java

@@ -28,3 +28,6 @@ public class LogoutReqVo implements Serializable {
 
 }
 
+
+
+

+ 53 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/SubmitFeedbackReqVo.java

@@ -0,0 +1,53 @@
+package com.sckw.system.model.vo.req;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 提交意见反馈请求对象
+ * @author chenxiaofei
+ * @date 2026-01-12
+ */
+@Data
+@Schema(description = "提交意见反馈请求对象")
+public class SubmitFeedbackReqVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    @NotNull(message = "用户ID不能为空")
+    @Schema(description = "用户ID", required = true, example = "1234567890")
+    private Long userId;
+
+    /**
+     * 反馈内容
+     */
+    @NotBlank(message = "反馈内容不能为空")
+    @Schema(description = "反馈内容", required = true, example = "系统操作不流畅,建议优化...")
+    private String content;
+
+    /**
+     * 图片附件地址列表
+     */
+    @Schema(description = "图片附件地址列表", example = "[\"https://example.com/image1.jpg\", \"https://example.com/image2.jpg\"]")
+    private List<String> images;
+
+    /**
+     * 联系方式
+     */
+    @Schema(description = "联系方式", example = "13800138000")
+    private String contactInfo;
+
+}
+
+
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/SwitchAccountReqVo.java

@@ -43,3 +43,6 @@ public class SwitchAccountReqVo implements Serializable {
 
 }
 
+
+
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/UserInfoReqVo.java

@@ -26,3 +26,6 @@ public class UserInfoReqVo implements Serializable {
 
 }
 
+
+
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/AppVersionsResVo.java

@@ -75,3 +75,6 @@ public class AppVersionsResVo implements Serializable {
 }
 
 
+
+
+

+ 86 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/FeedbackResVo.java

@@ -0,0 +1,86 @@
+package com.sckw.system.model.vo.res;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.sckw.core.utils.LongToStringUtils;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 意见反馈响应对象
+ * @author chenxiaofei
+ * @date 2026-01-12
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Schema(description = "意见反馈响应对象")
+public class FeedbackResVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 反馈ID
+     */
+    @JsonSerialize(using = LongToStringUtils.class)
+    @Schema(description = "反馈ID", example = "1234567890")
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    @JsonSerialize(using = LongToStringUtils.class)
+    @Schema(description = "用户ID", example = "1234567890")
+    private Long userId;
+
+    /**
+     * 反馈内容
+     */
+    @Schema(description = "反馈内容", example = "系统操作不流畅,建议优化...")
+    private String content;
+
+    /**
+     * 图片附件地址列表
+     */
+    @Schema(description = "图片附件地址列表")
+    private List<String> images;
+
+    /**
+     * 联系方式
+     */
+    @Schema(description = "联系方式", example = "13800138000")
+    private String contactInfo;
+
+    /**
+     * 是否已处理: 0-未处理, 1-已处理
+     */
+    @Schema(description = "是否已处理: 0-未处理, 1-已处理", example = "0")
+    private Integer isProcessed;
+
+    /**
+     * 处理状态描述
+     */
+    @Schema(description = "处理状态描述", example = "未处理")
+    private String processedDesc;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @Schema(description = "创建时间", example = "2026-01-12 10:30:00")
+    private Date createdAt;
+
+}
+
+
+

+ 70 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/LoginResVo.java

@@ -0,0 +1,70 @@
+package com.sckw.system.model.vo.res;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 登录响应对象
+ * @author chenxiaofei
+ * @date 2026-01-07
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Schema(description = "登录响应对象")
+public class LoginResVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    @Schema(description = "用户ID", example = "1234567890")
+    private Long userId;
+
+    /**
+     * 用户姓名
+     */
+    @Schema(description = "用户姓名", example = "张三")
+    private String name;
+
+    /**
+     * 账号
+     */
+    @Schema(description = "账号", example = "13800138000")
+    private String account;
+
+    /**
+     * 手机号
+     */
+    @Schema(description = "手机号", example = "13800138000")
+    private String phone;
+
+    /**
+     * 头像
+     */
+    @Schema(description = "头像URL", example = "https://example.com/avatar.jpg")
+    private String photo;
+
+    /**
+     * 角色名称
+     */
+    @Schema(description = "角色名称", example = "管理员")
+    private String roleName;
+
+    /**
+     * 系统类型
+     */
+    @Schema(description = "系统类型: 1-运营端, 2-企业端", example = "2")
+    private Integer systemType;
+
+}
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/SwitchAccountResVo.java

@@ -68,3 +68,6 @@ public class SwitchAccountResVo implements Serializable {
 
 }
 
+
+
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/SysArticlesResVo.java

@@ -60,3 +60,6 @@ public class SysArticlesResVo implements Serializable {
 }
 
 
+
+
+

+ 3 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/UserInfoResVo.java

@@ -69,3 +69,6 @@ public class UserInfoResVo implements Serializable {
 
 }
 
+
+
+

+ 0 - 88
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/res/UsersResVo.java

@@ -1,88 +0,0 @@
-package com.sckw.system.model.vo.res;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.sckw.core.utils.LongToStringUtils;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-
-import java.io.Serial;
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * 用户信息返回对象
- * @author chenxiaofei
- * @date 2026-01-07
- */
-@Data
-@Builder
-@AllArgsConstructor
-@Schema(description = "用户信息返回对象")
-public class UsersResVo implements Serializable {
-
-    @Serial
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 雪花ID
-     */
-    @JsonSerialize(using = LongToStringUtils.class)
-    @Schema(description = "雪花ID")
-    private Long id;
-
-    /**
-     * 登录账号
-     */
-    @Schema(description = "登录账号")
-    private String username;
-
-    /**
-     * 用户姓名
-     */
-    @Schema(description = "用户姓名")
-    private String fullName;
-
-    /**
-     * 头像URL地址
-     */
-    @Schema(description = "头像URL地址")
-    private String avatarUrl;
-
-    /**
-     * 联系电话
-     */
-    @Schema(description = "联系电话")
-    private String phone;
-
-    /**
-     * 角色: 1-铲车司机(监管员), 2-门卫
-     */
-    @Schema(description = "角色: 1-铲车司机(监管员), 2-门卫")
-    private Integer roleType;
-
-    /**
-     * 状态: 0-禁用, 1-启用
-     */
-    @Schema(description = "状态: 0-禁用, 1-启用")
-    private Integer status;
-
-    /**
-     * 注册时间
-     */
-    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-    @Schema(description = "注册时间")
-    private Date createdAt;
-
-    /**
-     * 更新时间
-     */
-    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-    @Schema(description = "更新时间")
-    private Date updatedAt;
-
-}
-
-

+ 154 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsFeedbackService.java

@@ -0,0 +1,154 @@
+package com.sckw.system.service;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson2.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.sckw.core.exception.SystemException;
+import com.sckw.core.web.constant.HttpStatus;
+import com.sckw.system.model.Feedbacks;
+import com.sckw.system.model.vo.req.FeedbackListReqVo;
+import com.sckw.system.model.vo.req.SubmitFeedbackReqVo;
+import com.sckw.system.model.vo.res.FeedbackResVo;
+import com.sckw.system.repository.FeedbacksService;
+import com.sckw.system.repository.KwsUserRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * 意见反馈业务Service实现类
+ * @author chenxiaofei
+ * @date 2026-01-12
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class KwsFeedbackService {
+
+    private final FeedbacksService feedbacksService;
+    private final KwsUserRepository kwsUserRepository;
+
+    @Transactional(rollbackFor = Exception.class)
+    public FeedbackResVo submitFeedback(SubmitFeedbackReqVo reqVo) {
+        log.info("提交意见反馈,用户ID: {}", reqVo.getUserId());
+
+        // 1. 校验用户是否存在
+        if (Objects.isNull(kwsUserRepository.getById(reqVo.getUserId()))) {
+            throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "用户不存在");
+        }
+
+        // 2. 构建反馈实体
+        Feedbacks feedback = new Feedbacks();
+        feedback.setUserId(reqVo.getUserId());
+        feedback.setContent(reqVo.getContent());
+        
+        // 3. 处理图片列表,转换为JSON字符串
+        if (CollUtil.isNotEmpty(reqVo.getImages())) {
+            feedback.setImages(JSON.toJSONString(reqVo.getImages()));
+        } else {
+            feedback.setImages("[]");
+        }
+        
+        feedback.setContactInfo(StrUtil.isNotBlank(reqVo.getContactInfo()) ? reqVo.getContactInfo() : "");
+        // 默认未处理
+        feedback.setIsProcessed(0);
+        feedback.setCreatedAt(new Date());
+
+        // 4. 保存到数据库
+        if (!feedbacksService.save(feedback)) {
+            throw new SystemException(HttpStatus.CRUD_FAIL_CODE, "提交反馈失败");
+        }
+
+        log.info("意见反馈提交成功,反馈ID: {}", feedback.getId());
+
+        // 5. 返回响应对象
+        return convertToResVo(feedback);
+    }
+
+
+    public Page<FeedbackResVo> getFeedbackList(FeedbackListReqVo reqVo) {
+        log.info("查询用户反馈列表,用户ID: {}, 页码: {}, 每页数量: {}", 
+                reqVo.getUserId(), reqVo.getPageNum(), reqVo.getPageSize());
+
+        // 1. 校验用户是否存在
+        if (Objects.isNull(kwsUserRepository.getById(reqVo.getUserId()))) {
+            throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "用户不存在");
+        }
+
+        // 2. 构建分页查询条件
+        Page<Feedbacks> page = new Page<>(reqVo.getPageNum(), reqVo.getPageSize());
+        LambdaQueryWrapper<Feedbacks> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(Feedbacks::getUserId, reqVo.getUserId())
+                    .orderByDesc(Feedbacks::getCreatedAt);
+
+        // 3. 执行查询
+        Page<Feedbacks> feedbackPage = feedbacksService.page(page, queryWrapper);
+
+        // 4. 转换为响应对象
+        Page<FeedbackResVo> resPage = new Page<>(feedbackPage.getCurrent(), feedbackPage.getSize(), feedbackPage.getTotal());
+        List<FeedbackResVo> resVoList = feedbackPage.getRecords().stream()
+                .map(this::convertToResVo)
+                .collect(Collectors.toList());
+        resPage.setRecords(resVoList);
+
+        log.info("查询反馈列表成功,共 {} 条记录", resPage.getTotal());
+        return resPage;
+    }
+
+
+    public FeedbackResVo getFeedbackDetail(Long feedbackId) {
+        log.info("查询反馈详情,反馈ID: {}", feedbackId);
+
+        // 1. 根据ID查询反馈
+        Feedbacks feedback = feedbacksService.getById(feedbackId);
+        if (Objects.isNull(feedback)) {
+            throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "反馈记录不存在");
+        }
+
+        // 2. 转换为响应对象
+        return convertToResVo(feedback);
+    }
+
+    /**
+     * 将实体对象转换为响应对象
+     * @param feedback 反馈实体
+     * @return FeedbackResVo
+     */
+    private FeedbackResVo convertToResVo(Feedbacks feedback) {
+        // 解析图片JSON字符串为列表
+        List<String> imageList = new ArrayList<>();
+        if (StrUtil.isNotBlank(feedback.getImages())) {
+            try {
+                imageList = JSON.parseArray(feedback.getImages(), String.class);
+            } catch (Exception e) {
+                log.warn("解析图片JSON失败,反馈ID: {}, images: {}", feedback.getId(), feedback.getImages(), e);
+                imageList = new ArrayList<>();
+            }
+        }
+
+        // 处理状态描述
+        String processedDesc = feedback.getIsProcessed() == 1 ? "已处理" : "未处理";
+
+        return FeedbackResVo.builder()
+                .id(feedback.getId())
+                .userId(feedback.getUserId())
+                .content(feedback.getContent())
+                .images(imageList)
+                .contactInfo(feedback.getContactInfo())
+                .isProcessed(feedback.getIsProcessed())
+                .processedDesc(processedDesc)
+                .createdAt(feedback.getCreatedAt())
+                .build();
+    }
+
+}
+

+ 261 - 3
sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsUserService.java

@@ -1,6 +1,8 @@
 package com.sckw.system.service;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.sckw.core.common.enums.enums.DictEnum;
@@ -12,31 +14,39 @@ import com.sckw.core.model.enums.ClientTypeEnum;
 import com.sckw.core.model.enums.SystemTypeEnum;
 import com.sckw.core.utils.*;
 import com.sckw.core.web.constant.HttpStatus;
+import com.sckw.core.web.constant.RequestConstant;
 import com.sckw.core.web.context.LoginUserHolder;
+import com.sckw.core.web.model.LoginUserInfo;
+import com.sckw.core.web.request.RequestUtil;
 import com.sckw.excel.utils.ExcelUtil;
 import com.sckw.redis.constant.RedisConstant;
 import com.sckw.redis.utils.RedissonUtils;
 import com.sckw.system.api.model.dto.req.RegisterReqDto;
 import com.sckw.system.api.model.dto.req.UpdatePasswordReqDto;
 import com.sckw.system.api.model.dto.res.AreaTreeFrontResDto;
+import com.sckw.system.api.model.dto.res.EntCacheResDto;
 import com.sckw.system.api.model.dto.res.KwsUserResDto;
 import com.sckw.system.api.model.dto.res.RegisterResDto;
 import com.sckw.system.api.model.dto.res.SysDictResDto;
 import com.sckw.system.dao.*;
 import com.sckw.system.dubbo.RemoteSystemServiceImpl;
 import com.sckw.system.model.*;
+import com.sckw.system.model.LoginLogs;
 import com.sckw.system.api.model.dto.res.RoleInfoDto;
 import com.sckw.system.model.report.KwsUserExcel;
 import com.sckw.system.model.vo.req.*;
 import com.sckw.system.model.vo.res.KwsUserResVo;
 import com.sckw.system.model.vo.res.KwsUserSystemTypeVo;
+import com.sckw.system.model.vo.res.LoginResVo;
 import com.sckw.system.model.vo.res.SalesResp;
 import com.sckw.system.model.vo.res.SwitchAccountResVo;
 import com.sckw.system.model.vo.res.UserInfoResVo;
 import com.sckw.system.repository.KwsRoleRepository;
 import com.sckw.system.repository.KwsUserRepository;
 import com.sckw.system.repository.KwsUserRoleRepository;
+import com.sckw.system.repository.LoginLogsService;
 import com.sckw.transport.api.dubbo.TransportRemoteStatisticsService;
+import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -98,6 +108,8 @@ public class KwsUserService {
 
     private final KwsUserRepository kwsUserRepository;
 
+    private final LoginLogsService loginLogsService;
+
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private TransportRemoteStatisticsService transportStatisticsService;
 
@@ -875,6 +887,7 @@ public class KwsUserService {
         String account = kwsUser.getAccount();
         String currentPwd = kwsUser.getPassword();
         String salt = kwsUser.getSalt();
+        oldPassword =  PasswordUtils.md5(oldPassword);
         checkPassword(account, oldPassword, currentPwd, salt);
 
         // 3. 校验新密码是否与旧密码相同
@@ -883,20 +896,108 @@ public class KwsUserService {
         }
 
         // 4. 修改密码
+        newPassword =  PasswordUtils.md5(newPassword);
         updatePwd(newPassword, kwsUser);
         
         log.info("密码修改成功,用户ID: {}", userId);
     }
 
+    /**
+     * 用户登录(叉车APP)
+     *
+     * @param account 账号
+     * @param password 密码
+     * @param systemType 系统类型
+     * @param request HTTP请求(用于记录登录日志)
+     * @return LoginResVo
+     */
+    public LoginResVo login(String account, String password, Integer systemType, HttpServletRequest request) {
+        log.info("用户登录,账号: {}, 系统类型: {}", account, systemType);
+        
+        // 1. 查询用户信息
+        KwsUser kwsUser = getUserByAccount(account, systemType);
+        if (Objects.isNull(kwsUser)) {
+            throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "账号不存在");
+        }
+
+        // 2. 检查用户状态
+        if (kwsUser.getDelFlag() != Global.NO) {
+            throw new SystemException(HttpStatus.QUERY_FAIL_CODE, "账号已删除");
+        }
+        if (kwsUser.getStatus() != null && kwsUser.getStatus() == Global.YES) {
+            throw new SystemException(HttpStatus.CODE_10301, "账号已被锁定");
+        }
+
+        // 3. 校验密码
+        checkPassword(account, password, kwsUser.getPassword(), kwsUser.getSalt());
+
+        // 4. 查询用户角色
+        List<RoleInfoDto> roleInfoList = kwsUserRoleDao.queryRoleList(kwsUser.getId());
+        String roleName = "";
+        Long roleId = null;
+        if (CollUtil.isNotEmpty(roleInfoList)) {
+            roleName = roleInfoList.stream()
+                    .map(RoleInfoDto::getRoleName)
+                    .collect(Collectors.joining(","));
+            roleId = roleInfoList.get(0).getRoleId();
+        }
+
+        // 5. 获取客户端类型
+        String clientType = request != null ? request.getHeader(RequestConstant.CLIENT_TYPE) : null;
+        if (StringUtils.isBlank(clientType)) {
+            clientType = ClientTypeEnum.app.getValue(); // 默认使用app
+        }
+
+        // 6. 生成token并缓存
+        if (request != null) {
+            generateToken(kwsUser.getId(), account, clientType, systemType);
+        }
+
+        // 7. 缓存用户登录信息
+        if (request != null) {
+            saveUserToCache(kwsUser, systemType, clientType, roleId);
+        }
+
+        // 8. 缓存企业信息
+        if (request != null && kwsUser.getEntId() != null) {
+            saveEntToCache(kwsUser.getEntId());
+        }
+
+        // 9. 缓存菜单权限
+        if (request != null && roleId != null) {
+            saveMenusToCache(kwsUser.getId(), systemType, roleId);
+        }
+
+        // 10. 组装返回结果
+        LoginResVo result = LoginResVo.builder()
+                .userId(kwsUser.getId())
+                .name(kwsUser.getName())
+                .account(kwsUser.getAccount())
+                .phone(kwsUser.getPhone())
+                .photo(kwsUser.getPhoto())
+                .roleName(roleName)
+                .systemType(kwsUser.getSystemType())
+                .build();
+
+        // 11. 记录登录日志(首次登录)
+        if (request != null) {
+            saveLoginLogs(kwsUser.getId(), request, 1);
+        }
+
+        log.info("登录成功,用户ID: {}", kwsUser.getId());
+        return result;
+    }
+
     /**
      * 切换账号(叉车APP)
      *
      * @param account 账号
      * @param password 密码
      * @param systemType 系统类型
+     * @param request HTTP请求(可选,用于记录登录日志)
      * @return SwitchAccountResVo
      */
-    public SwitchAccountResVo switchAccount(String account, String password, Integer systemType) {
+    public SwitchAccountResVo switchAccount(String account, String password, Integer systemType, HttpServletRequest request) {
         log.info("切换账号,账号: {}, 系统类型: {}", account, systemType);
         
         // 1. 查询用户信息
@@ -926,8 +1027,7 @@ public class KwsUserService {
         }
 
         // 5. 组装返回结果
-        log.info("切换账号成功,用户ID: {}", kwsUser.getId());
-        return SwitchAccountResVo.builder()
+        SwitchAccountResVo result = SwitchAccountResVo.builder()
                 .userId(kwsUser.getId())
                 .name(kwsUser.getName())
                 .account(kwsUser.getAccount())
@@ -936,6 +1036,14 @@ public class KwsUserService {
                 .roleName(roleName)
                 .systemType(kwsUser.getSystemType())
                 .build();
+
+        // 6. 记录切换账号登录日志
+        if (request != null) {
+            saveLoginLogs(kwsUser.getId(), request, 2);
+        }
+
+        log.info("切换账号成功,用户ID: {}", kwsUser.getId());
+        return result;
     }
 
     /**
@@ -964,4 +1072,154 @@ public class KwsUserService {
 
         log.info("退出登录成功,用户ID: {}", userId);
     }
+
+    /**
+     * 生成token并缓存
+     * @param userId 用户ID
+     * @param account 账号
+     * @param clientType 客户端类型
+     * @param systemType 系统类型
+     * @return token
+     */
+    private String generateToken(Long userId, String account, String clientType, Integer systemType) {
+        Map<String, Object> info = new HashMap<>(Global.NUMERICAL_SIXTEEN);
+        info.put("userId", userId);
+        info.put("account", account);
+        info.put("clientType", clientType);
+        info.put("systemType", systemType);
+        String key = Global.getFullUserTokenKey(clientType, userId);
+        String token = EncryUtil.encryV1(Global.PRI_KEY, JSON.toJSONString(info));
+        int expireTime = ClientTypeEnum.expireTime(clientType);
+        RedissonUtils.putString(key, token, expireTime);
+        return token;
+    }
+
+    /**
+     * 缓存用户登录信息
+     * @param kwsUser 用户信息
+     * @param systemType 系统类型
+     * @param clientType 客户端类型
+     * @param roleId 角色ID
+     */
+    private void saveUserToCache(KwsUser kwsUser, Integer systemType, String clientType, Long roleId) {
+        try {
+            LoginUserInfo loginUserInfo = new LoginUserInfo();
+            loginUserInfo.setId(kwsUser.getId());
+            loginUserInfo.setSystemType(systemType);
+            loginUserInfo.setClientType(clientType);
+            loginUserInfo.setAccount(kwsUser.getAccount());
+            loginUserInfo.setUserName(kwsUser.getName());
+            loginUserInfo.setPhone(kwsUser.getPhone());
+            loginUserInfo.setStatus(kwsUser.getStatus());
+            loginUserInfo.setIsMain(kwsUser.getIsMain() != null ? kwsUser.getIsMain() : Global.NO);
+            loginUserInfo.setEntId(kwsUser.getEntId());
+            loginUserInfo.setUseRoleId(roleId);
+            loginUserInfo.setUseEntId(kwsUser.getEntId());
+
+            // 查询企业信息
+            if (kwsUser.getEntId() != null) {
+                EntCacheResDto enterprise = remoteSystemService.queryEntDetails(kwsUser.getEntId());
+                if (enterprise != null) {
+                    loginUserInfo.setEntName(enterprise.getFirmName());
+                }
+            }
+
+            // 查询数据权限(暂时不实现,需要时可以通过RemoteUserService调用)
+            // 如果需要数据权限,可以通过Dubbo调用RemoteUserService.queryAuthUserList
+
+            String key = Global.getFullUserLoginKey(systemType, kwsUser.getId());
+            RedissonUtils.putString(key, JSON.toJSONString(loginUserInfo), Global.APP_TOKEN_EXPIRE);
+        } catch (Exception e) {
+            log.error("缓存用户登录信息失败,用户ID: {}", kwsUser.getId(), e);
+        }
+    }
+
+    /**
+     * 缓存企业信息
+     * @param entId 企业ID
+     */
+    private void saveEntToCache(Long entId) {
+        try {
+            EntCacheResDto enterprise = remoteSystemService.queryEntDetails(entId);
+            if (enterprise != null) {
+                String key = Global.getFullUserEntKey(entId);
+                RedissonUtils.putString(key, JSON.toJSONString(enterprise), Global.APP_TOKEN_EXPIRE);
+            }
+        } catch (Exception e) {
+            log.error("缓存企业信息失败,企业ID: {}", entId, e);
+        }
+    }
+
+    /**
+     * 缓存菜单权限
+     * @param userId 用户ID
+     * @param systemType 系统类型
+     * @param roleId 角色ID
+     */
+    private void saveMenusToCache(Long userId, Integer systemType, Long roleId) {
+        try {
+            // 使用本地服务查询菜单
+            com.sckw.system.model.pojo.FindMenuTreePojo findMenuTreePojo = new com.sckw.system.model.pojo.FindMenuTreePojo();
+            findMenuTreePojo.setRoleIds(Collections.singletonList(roleId));
+            
+            List<com.sckw.system.model.vo.res.KwsMenuResVo> kwsMenuResVos = kwsMenuService.findList(findMenuTreePojo);
+            if (CollectionUtils.isEmpty(kwsMenuResVos)) {
+                RedissonUtils.delete(Global.REDIS_SYS_MENU_PREFIX + systemType + Global.COLON + userId);
+                log.warn("未查询到角色{}的菜单权限", roleId);
+                return;
+            }
+
+            List<String> menus = new ArrayList<>();
+            for (com.sckw.system.model.vo.res.KwsMenuResVo menuResVo : kwsMenuResVos) {
+                String links = menuResVo.getLinks();
+                if (StringUtils.isNotBlank(links)) {
+                    menus.addAll(Arrays.asList(links.split(",")));
+                }
+            }
+            RedissonUtils.putSet(Global.REDIS_SYS_MENU_PREFIX + userId, menus);
+        } catch (Exception e) {
+            log.error("缓存菜单权限失败,用户ID: {}, 角色ID: {}", userId, roleId, e);
+        }
+    }
+
+    /**
+     * 保存登录日志
+     * @param userId 用户ID
+     * @param request HTTP请求
+     * @param loginType 登录类型: 1-首次登录, 2-切换账号登录
+     */
+    public void saveLoginLogs(Long userId, HttpServletRequest request, Integer loginType) {
+        try {
+            // 获取登录IP
+            String loginIp = RequestUtil.getClientIp(request);
+            
+            // 获取设备信息
+            String userAgentStr = request.getHeader("User-Agent");
+            String deviceInfo = "";
+            if (userAgentStr != null && !userAgentStr.isEmpty()) {
+                try {
+                    UserAgent userAgent = UserAgentUtil.parse(userAgentStr);
+                    String browser = userAgent.getBrowser().getName();
+                    String browserVersion = userAgent.getVersion();
+                    String os = userAgent.getOs().getName();
+                    String device = userAgent.getPlatform().getName();
+                    deviceInfo = String.format("%s %s / %s / %s", browser, browserVersion, os, device);
+                } catch (Exception e) {
+                    deviceInfo = userAgentStr.length() > 100 ? userAgentStr.substring(0, 100) : userAgentStr;
+                }
+            }
+
+            LoginLogs loginLogs = new LoginLogs();
+            loginLogs.setId(new IdWorker(1).nextId());
+            loginLogs.setUserId(userId);
+            loginLogs.setLoginIp(loginIp != null ? loginIp : "");
+            loginLogs.setDeviceInfo(deviceInfo);
+            loginLogs.setLoginType(loginType);
+            loginLogs.setLoginTime(new Date());
+            loginLogsService.save(loginLogs);
+        } catch (Exception e) {
+            // 记录日志失败不影响主流程
+            log.error("保存登录日志失败,用户ID: {}", userId, e);
+        }
+    }
 }

+ 0 - 16
sql/2026/01/2026_01_07_chenxiaofei_create.sql

@@ -1,19 +1,3 @@
--- 1. 用户基础信息表
-CREATE TABLE `users` (
-                         `id` BIGINT UNSIGNED PRIMARY KEY COMMENT '雪花ID',
-                         `ent_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联企业ID',
-                         `username` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '登录账号',
-                         `password_hash` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '加密存储的密码',
-                         `full_name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '用户姓名',
-                         `avatar_url` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '头像URL地址',
-                         `phone` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '联系电话',
-                         `role_type` TINYINT NOT NULL DEFAULT 1 COMMENT '角色: 1-铲车司机(监管员), 2-门卫',
-                         `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态: 0-注册, 1-启用, 2-注销',
-                         `reason` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '注销原因',
-                         `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',
-                         `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-                         UNIQUE KEY `uk_username` (`username`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
 
 -- 2. 登录日志表
 CREATE TABLE `login_logs` (