diff --git a/pom.xml b/pom.xml
index 52e743f..32b9b26 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,6 +32,9 @@
8.0.3
4.8.1
+ 4.3.0
+ 0.11.5
+
3.0.13
1.18.30
@@ -120,10 +123,34 @@
hutool-all
5.8.3
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${jjwt.version}
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ ${jjwt.version}
+ runtime
+
com.baomidou
dynamic-datasource-spring-boot3-starter
- 4.2.0
+ ${dynamic-datasource.version}
org.mybatis
@@ -177,4 +204,28 @@
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+ repackage
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
diff --git a/src/main/java/com/project/base/domain/dto/BaseDTO.java b/src/main/java/com/project/base/domain/dto/BaseDTO.java
index 044db63..4289518 100644
--- a/src/main/java/com/project/base/domain/dto/BaseDTO.java
+++ b/src/main/java/com/project/base/domain/dto/BaseDTO.java
@@ -11,7 +11,6 @@ import java.util.function.Supplier;
@Data
public class BaseDTO {
- private Long id;
private Long creatorId;
diff --git a/src/main/java/com/project/base/domain/entity/BaseEntity.java b/src/main/java/com/project/base/domain/entity/BaseEntity.java
index 895bbb4..ee6320c 100644
--- a/src/main/java/com/project/base/domain/entity/BaseEntity.java
+++ b/src/main/java/com/project/base/domain/entity/BaseEntity.java
@@ -17,10 +17,7 @@ import java.util.function.Supplier;
@Data
public class BaseEntity implements Serializable {
- @Id
- @TableId(value = "id" , type = IdType.ASSIGN_ID)
- @Column(name = "id" , columnDefinition="bigint(20)")
- private Long id;
+
@TableField(value = "creator_id" , fill = FieldFill.INSERT)
@Column(name = "creator_id" , columnDefinition="bigint(20) comment '创建用户id'")
diff --git a/src/main/java/com/project/ding/auth/DingTalkAuthenticationProvider.java b/src/main/java/com/project/ding/auth/DingTalkAuthenticationProvider.java
new file mode 100644
index 0000000..456aa8a
--- /dev/null
+++ b/src/main/java/com/project/ding/auth/DingTalkAuthenticationProvider.java
@@ -0,0 +1,68 @@
+package com.project.ding.auth;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.project.ding.domain.dto.UserDTO;
+import com.project.ding.domain.entity.AdminWhiteListEntity;
+import com.project.ding.domain.entity.UserEntity;
+import com.project.ding.mapper.AdminWhiteListMapper;
+import com.project.ding.mapper.UserMapper;
+import com.project.ding.utils.DingUserSyncUtil;
+import com.project.ding.utils.DingUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+@Component
+public class DingTalkAuthenticationProvider implements AuthenticationProvider {
+ @Autowired
+ private DingUtil dingUtil; // 封装获取 userId 接口
+ @Autowired
+ private UserMapper userMapper;
+ @Autowired
+ private AdminWhiteListMapper whitelistMapper;
+
+ @Autowired
+ private DingUserSyncUtil dingUserSyncUtil;
+
+ @Override
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+ String authCode = (String) authentication.getPrincipal();
+ // 1. 换取钉钉 UserId
+ String dingId = dingUtil.getUserIdByCode(authCode);
+ if (StrUtil.isBlank(dingId)) {
+ throw new BadCredentialsException("钉钉授权失败");
+ }
+
+ UserEntity user = userMapper.selectById(Long.valueOf(dingId));
+ if (Objects.isNull(user)) {
+ // 直接插入这个用户并且触发一次异步的全量用户同步
+ UserDTO userDTO = dingUtil.getUserById(dingId);
+ if (Objects.isNull(userDTO)) {
+ throw new BadCredentialsException("获取用户信息异常,请联系管理员");
+ }
+ userMapper.batchUpsert(Collections.singletonList(userDTO.toEntity(UserEntity::new)));
+ user = userMapper.selectById(Long.valueOf(dingId));
+ dingUserSyncUtil.triggerSync(false);
+ }
+
+ // 3. 分配角色:默认考生,白名单为管理员
+ List authorities = new ArrayList<>();
+ authorities.add(new SimpleGrantedAuthority("ROLE_CANDIDATE"));
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(AdminWhiteListEntity::getUserId , user.getId());
+ AdminWhiteListEntity existOne = whitelistMapper.selectOne(queryWrapper);
+ if (Objects.nonNull(existOne)) {
+ authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
+ }
+ return new DingTalkAuthenticationToken(user, authorities);
+ }
+
+ @Override public boolean supports(Class> a) { return DingTalkAuthenticationToken.class.isAssignableFrom(a); }
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/auth/DingTalkAuthenticationToken.java b/src/main/java/com/project/ding/auth/DingTalkAuthenticationToken.java
new file mode 100644
index 0000000..b88ec97
--- /dev/null
+++ b/src/main/java/com/project/ding/auth/DingTalkAuthenticationToken.java
@@ -0,0 +1,25 @@
+package com.project.ding.auth;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.Collection;
+
+public class DingTalkAuthenticationToken extends AbstractAuthenticationToken {
+ private final Object principal; // 登录后存 UserEntity,登录前存 authCode
+
+ public DingTalkAuthenticationToken(Object principal) {
+ super(null);
+ this.principal = principal;
+ setAuthenticated(false);
+ }
+
+ public DingTalkAuthenticationToken(Object principal, Collection extends GrantedAuthority> authorities) {
+ super(authorities);
+ this.principal = principal;
+ setAuthenticated(true);
+ }
+
+ @Override public Object getCredentials() { return null; }
+ @Override public Object getPrincipal() { return principal; }
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/auth/JwtAuthenticationFilter.java b/src/main/java/com/project/ding/auth/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..d32b78b
--- /dev/null
+++ b/src/main/java/com/project/ding/auth/JwtAuthenticationFilter.java
@@ -0,0 +1,61 @@
+package com.project.ding.auth;
+
+import com.project.ding.utils.JwtUtils;
+import io.jsonwebtoken.Claims;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority; // 确保有这行导入
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+import java.util.List;
+
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+ @Autowired
+ private JwtUtils jwtUtils;
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+
+ String header = request.getHeader("Authorization");
+
+ if (StringUtils.hasText(header) && header.startsWith("Bearer ")) {
+ String token = header.substring(7);
+ try {
+ Claims claims = jwtUtils.parseToken(token);
+ String userId = claims.getSubject();
+
+ // 1. 获取原始的 roles 列表
+ Object rolesObj = claims.get("roles");
+
+ if (userId != null && rolesObj instanceof List>) {
+ // 2. 显式转换并映射,增加 Object::toString 保证类型安全
+ List authorities = ((List>) rolesObj).stream()
+ .map(Object::toString) // 强制转为 String,解决构造器匹配问题
+ .map(SimpleGrantedAuthority::new) // 现在这里不会报红了
+ .toList();
+
+ // 3. 构建并设置认证信息
+ UsernamePasswordAuthenticationToken authentication =
+ new UsernamePasswordAuthenticationToken(userId, null, authorities);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ } catch (Exception e) {
+ // Token解析失败不处理,后续 Security 拦截器会返回 403
+ logger.error("JWT authentication failed: " + e.getMessage());
+ }
+ }
+
+ filterChain.doFilter(request, response);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/config/DingProperties.java b/src/main/java/com/project/ding/config/DingProperties.java
new file mode 100644
index 0000000..2b14ce2
--- /dev/null
+++ b/src/main/java/com/project/ding/config/DingProperties.java
@@ -0,0 +1,14 @@
+package com.project.ding.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "ding")
+public class DingProperties {
+ private String appKey;
+ private String appSecret;
+ private Long agentId;
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/config/DingConfig.java b/src/main/java/com/project/ding/config/DtConfig.java
similarity index 55%
rename from src/main/java/com/project/ding/config/DingConfig.java
rename to src/main/java/com/project/ding/config/DtConfig.java
index a5bd050..bddbbcc 100644
--- a/src/main/java/com/project/ding/config/DingConfig.java
+++ b/src/main/java/com/project/ding/config/DtConfig.java
@@ -3,21 +3,30 @@ package com.project.ding.config;
import com.github.tingyugetc520.ali.dingtalk.api.DtService;
import com.github.tingyugetc520.ali.dingtalk.api.impl.DtServiceImpl;
import com.github.tingyugetc520.ali.dingtalk.config.impl.DtDefaultConfigImpl;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
-public class DingConfig {
+public class DtConfig {
+
+ @Autowired
+ private DingProperties dingTalkProperties;
+
@Bean
public DtService dtService() {
DtDefaultConfigImpl config = new DtDefaultConfigImpl();
- config.setAppKey("ding4eootvq4jzas96lr");
- config.setAppSecret("UI6XcD8GFPE_W_yo2eQkMuoSsTEf1whpZYEXrsDA7CV7bkJp40B3VNcETWS1aGgg");
- // 如果是企业内部应用,通常还需要 AgentId ,
-// config.setAgentId(4145005949L);
+
+ // 直接从属性对象中取值
+ config.setAppKey(dingTalkProperties.getAppKey());
+ config.setAppSecret(dingTalkProperties.getAppSecret());
+
+ if (dingTalkProperties.getAgentId() != null) {
+ config.setAgentId(dingTalkProperties.getAgentId());
+ }
DtService dtService = new DtServiceImpl();
dtService.setDtConfigStorage(config);
- return dtService;//
+ return dtService;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/config/SecurityConfig.java b/src/main/java/com/project/ding/config/SecurityConfig.java
new file mode 100644
index 0000000..6f1c36a
--- /dev/null
+++ b/src/main/java/com/project/ding/config/SecurityConfig.java
@@ -0,0 +1,46 @@
+package com.project.ding.config;
+
+import com.project.ding.auth.DingTalkAuthenticationProvider;
+import com.project.ding.auth.JwtAuthenticationFilter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+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;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+@Configuration
+@EnableWebSecurity
+@EnableMethodSecurity
+public class SecurityConfig {
+
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http,
+ DingTalkAuthenticationProvider provider,
+ JwtAuthenticationFilter jwtFilter) throws Exception {
+ return http
+ .csrf(AbstractHttpConfigurer::disable)
+ .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .authorizeHttpRequests(auth -> auth
+ // 登录放行
+ .requestMatchers("/api/login/**").permitAll()
+ // 管理端锁定
+ .requestMatchers("/api/admin/**").hasRole("ADMIN")
+ // 其余(考生端)需登录
+ .anyRequest().authenticated()
+ )
+ .authenticationProvider(provider)
+ .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
+ .build();
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
+ return config.getAuthenticationManager();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/controller/DepartmentController.java b/src/main/java/com/project/ding/controller/DepartmentController.java
deleted file mode 100644
index 211e211..0000000
--- a/src/main/java/com/project/ding/controller/DepartmentController.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.project.ding.controller;
-
-
-import com.github.tingyugetc520.ali.dingtalk.api.DtService;
-import com.project.ding.domain.dto.DepartmentDTO;
-import com.project.ding.domain.dto.DingUserDTO;
-import com.project.ding.utils.DingUtil;
-import jakarta.servlet.http.HttpServletResponse;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.ArrayList;
-import java.util.List;
-
-
-@RestController
-@Slf4j
-public class DepartmentController {
- @Autowired
- private DingUtil dingUtil;
- @Autowired
- private DtService dtService;
- @GetMapping("/test")
- public void test(HttpServletResponse response) throws Exception {
- List list = dingUtil.getAllDepartment();
-
- List userList = new ArrayList<>();
- for (int i = 0; i < list.size(); i++) {
- List userInDepartment = dingUtil.getUserIdInDepartment(list.get(i).getId());
- userList.addAll(userInDepartment);
- }
- for (int i = 0; i < 10; i++) {
- System.out.println(111);
- }
- }
-}
diff --git a/src/main/java/com/project/ding/controller/LoginController.java b/src/main/java/com/project/ding/controller/LoginController.java
new file mode 100644
index 0000000..746bd39
--- /dev/null
+++ b/src/main/java/com/project/ding/controller/LoginController.java
@@ -0,0 +1,27 @@
+package com.project.ding.controller;
+
+import com.project.base.domain.result.Result;
+import com.project.ding.domain.dto.LoginDTO;
+import com.project.ding.domain.service.AuthDomainService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api/login")
+public class LoginController {
+
+ @Autowired
+ private AuthDomainService authDomainService;
+
+ /**
+ * 钉钉免登
+ */
+ @PostMapping("/ding")
+ public Result login(@RequestParam("authCode") String authCode) {
+ // 逻辑全部下沉到 Service
+ return Result.success(authDomainService.loginByDing(authCode));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/controller/SyncController.java b/src/main/java/com/project/ding/controller/SyncController.java
index 7c7ea5c..44fd1f1 100644
--- a/src/main/java/com/project/ding/controller/SyncController.java
+++ b/src/main/java/com/project/ding/controller/SyncController.java
@@ -9,7 +9,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
-@RequestMapping("/ding")
+@RequestMapping("/api/ding")
public class SyncController {
@Autowired
private DingUserSyncUtil dingUserSyncUtil;
diff --git a/src/main/java/com/project/ding/controller/UserController.java b/src/main/java/com/project/ding/controller/UserController.java
index 769fd55..b62571c 100644
--- a/src/main/java/com/project/ding/controller/UserController.java
+++ b/src/main/java/com/project/ding/controller/UserController.java
@@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
-@RequestMapping("/dingUser")
+@RequestMapping("/api/admin/dingUser")
public class UserController {
@Autowired
private UserApplicationService userApplicationService;
diff --git a/src/main/java/com/project/ding/domain/dto/DepartmentDTO.java b/src/main/java/com/project/ding/domain/dto/DepartmentDTO.java
index ef09bc9..bc33955 100644
--- a/src/main/java/com/project/ding/domain/dto/DepartmentDTO.java
+++ b/src/main/java/com/project/ding/domain/dto/DepartmentDTO.java
@@ -8,6 +8,7 @@ import java.util.List;
@Data
public class DepartmentDTO extends BaseDTO {
+ private Long id;
private String name;
private Long parentId;
diff --git a/src/main/java/com/project/ding/domain/dto/LoginDTO.java b/src/main/java/com/project/ding/domain/dto/LoginDTO.java
new file mode 100644
index 0000000..937959b
--- /dev/null
+++ b/src/main/java/com/project/ding/domain/dto/LoginDTO.java
@@ -0,0 +1,14 @@
+package com.project.ding.domain.dto;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@Builder
+public class LoginDTO {
+ private String token; // JWT 令牌
+ private String name; // 用户姓名
+ private List roles; // 用户角色列表
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/domain/dto/UserDTO.java b/src/main/java/com/project/ding/domain/dto/UserDTO.java
index f49fb48..593e9df 100644
--- a/src/main/java/com/project/ding/domain/dto/UserDTO.java
+++ b/src/main/java/com/project/ding/domain/dto/UserDTO.java
@@ -4,15 +4,17 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.DesensitizedUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.project.base.domain.dto.BaseDTO;
-import io.vavr.control.Try;
import lombok.Data;
import java.util.Date;
import java.util.List;
+import java.util.Objects;
import java.util.stream.Collectors;
@Data
public class UserDTO extends BaseDTO {
+ private String id;
+
private String unionId;
@@ -57,11 +59,14 @@ public class UserDTO extends BaseDTO {
public static UserDTO fromDingUserDTO(DingUserDTO dto) {
+ if (Objects.isNull(dto)) {
+ return null;
+ }
UserDTO userDTO = new UserDTO();
BeanUtil.copyProperties(dto , userDTO);
- userDTO.setId(Try.of(() -> Long.valueOf(dto.getUserId())).getOrElse(-1L));
userDTO.setDeptIdStr(dto.getDeptIdList().stream().map(Object::toString)
.collect(Collectors.joining(",")));
+ userDTO.setId(dto.getUserId());
return userDTO;
}
diff --git a/src/main/java/com/project/ding/domain/entity/AdminWhiteListEntity.java b/src/main/java/com/project/ding/domain/entity/AdminWhiteListEntity.java
new file mode 100644
index 0000000..83c7645
--- /dev/null
+++ b/src/main/java/com/project/ding/domain/entity/AdminWhiteListEntity.java
@@ -0,0 +1,40 @@
+package com.project.ding.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.project.base.domain.entity.BaseEntity;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.hibernate.annotations.Comment;
+
+/**
+ * 管理员白名单表
+ */
+@Data
+@Entity
+@Table(name = "evaluator_admin_white_list")
+@TableName("evaluator_admin_white_list")
+@EqualsAndHashCode(callSuper = true)
+public class AdminWhiteListEntity extends BaseEntity {
+
+ @TableId(value = "id" , type = IdType.ASSIGN_ID)
+ @Id
+ private Long id;
+
+ @Column(name = "user_id", nullable = false)
+ @Comment("用户ID (关联evaluator_user的id)")
+ private Long userId;
+
+ @Column(name = "user_name", length = 100)
+ @Comment("用户姓名 (冗余字段,方便管理后台直接查看)")
+ private String userName;
+
+ @Column(name = "admin_type")
+ @Comment("管理员类型")
+ private Integer adminType = 0;
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/domain/entity/DepartmentEntity.java b/src/main/java/com/project/ding/domain/entity/DepartmentEntity.java
index 4af423a..121a561 100644
--- a/src/main/java/com/project/ding/domain/entity/DepartmentEntity.java
+++ b/src/main/java/com/project/ding/domain/entity/DepartmentEntity.java
@@ -6,10 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.project.base.domain.entity.BaseEntity;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Index;
-import jakarta.persistence.Table;
+import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.JdbcTypeCode;
@@ -24,7 +21,9 @@ import java.util.List;
@TableName(value = "evaluator_department" , autoResultMap = true)
@EqualsAndHashCode(callSuper = true)
public class DepartmentEntity extends BaseEntity {
+
@TableId(type = IdType.INPUT)
+ @Id
private Long id;
@Column(name = "name" , columnDefinition="varchar(255) comment '部门名称'")
diff --git a/src/main/java/com/project/ding/domain/entity/SyncLogEntity.java b/src/main/java/com/project/ding/domain/entity/SyncLogEntity.java
index cb4664e..bdf4549 100644
--- a/src/main/java/com/project/ding/domain/entity/SyncLogEntity.java
+++ b/src/main/java/com/project/ding/domain/entity/SyncLogEntity.java
@@ -1,10 +1,13 @@
package com.project.ding.domain.entity;
+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.project.base.domain.entity.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -19,6 +22,9 @@ import java.util.Date;
@TableName("evaluator_sync_log")
@EqualsAndHashCode(callSuper = true)
public class SyncLogEntity extends BaseEntity {
+ @TableId(value = "id" , type = IdType.ASSIGN_ID)
+ @Id
+ private Long id;
@Comment("同步开始时间")
@Column(name = "start_time")
diff --git a/src/main/java/com/project/ding/domain/entity/UserEntity.java b/src/main/java/com/project/ding/domain/entity/UserEntity.java
index 8f838d3..1cbd400 100644
--- a/src/main/java/com/project/ding/domain/entity/UserEntity.java
+++ b/src/main/java/com/project/ding/domain/entity/UserEntity.java
@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.project.base.domain.entity.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -26,7 +27,9 @@ import java.util.List;
public class UserEntity extends BaseEntity {
@TableId(type = IdType.INPUT)
- private Long id;
+ @Column(length = 500)
+ @Id
+ private String id;
@Column(name = "union_id" , columnDefinition="varchar(255) comment '企业架构内唯一标识'")
@TableField("union_id")
diff --git a/src/main/java/com/project/ding/domain/service/AuthDomainService.java b/src/main/java/com/project/ding/domain/service/AuthDomainService.java
new file mode 100644
index 0000000..9b6fe60
--- /dev/null
+++ b/src/main/java/com/project/ding/domain/service/AuthDomainService.java
@@ -0,0 +1,10 @@
+package com.project.ding.domain.service;
+
+import com.project.ding.domain.dto.LoginDTO;
+
+public interface AuthDomainService {
+ /**
+ * 钉钉免登逻辑
+ */
+ LoginDTO loginByDing(String authCode);
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/domain/service/impl/AuthDomainServiceImpl.java b/src/main/java/com/project/ding/domain/service/impl/AuthDomainServiceImpl.java
new file mode 100644
index 0000000..11b5634
--- /dev/null
+++ b/src/main/java/com/project/ding/domain/service/impl/AuthDomainServiceImpl.java
@@ -0,0 +1,60 @@
+package com.project.ding.domain.service.impl;
+
+import com.project.base.domain.exception.BusinessErrorException;
+import com.project.ding.auth.DingTalkAuthenticationToken;
+import com.project.ding.domain.dto.LoginDTO;
+import com.project.ding.domain.entity.UserEntity;
+import com.project.ding.domain.service.AuthDomainService;
+import com.project.ding.utils.JwtUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.stereotype.Service;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.List;
+
+@Service
+@Slf4j
+public class AuthDomainServiceImpl implements AuthDomainService {
+
+ @Autowired
+ private AuthenticationManager authenticationManager;
+
+ @Autowired
+ private JwtUtils jwtUtils;
+
+ @Override
+ public LoginDTO loginByDing(String authCode) {
+ // 1. 构建未认证的内部 Token
+ DingTalkAuthenticationToken authToken = new DingTalkAuthenticationToken(authCode);
+
+ // 2. 委托 AuthenticationManager 进行认证
+ // 这一步会进入你之前写的 DingTalkAuthenticationProvider
+ Authentication authentication;
+ try {
+ authentication = authenticationManager.authenticate(authToken);
+ } catch (AuthenticationException e) {
+ log.warn("钉钉认证失败: {}", e.getMessage());
+ throw new BusinessErrorException("认证失败:" + e.getMessage()); // 抛出自定义业务异常
+ }
+
+ // 3. 认证成功后,从安全上下文中提取用户信息和权限
+ UserEntity user = (UserEntity) authentication.getPrincipal();
+ List roles = authentication.getAuthorities().stream()
+ .map(GrantedAuthority::getAuthority)
+ .toList();
+
+ // 4. 生成 JWT
+ String token = jwtUtils.createToken(user.getId(), roles);
+
+ // 5. 组装并返回结果
+ return LoginDTO.builder()
+ .token(token)
+ .name(user.getName())
+ .roles(roles)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/ding/mapper/AdminWhiteListMapper.java b/src/main/java/com/project/ding/mapper/AdminWhiteListMapper.java
new file mode 100644
index 0000000..2a61ab0
--- /dev/null
+++ b/src/main/java/com/project/ding/mapper/AdminWhiteListMapper.java
@@ -0,0 +1,10 @@
+package com.project.ding.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.project.ding.domain.entity.AdminWhiteListEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface AdminWhiteListMapper extends BaseMapper {
+}
diff --git a/src/main/java/com/project/ding/utils/DingUtil.java b/src/main/java/com/project/ding/utils/DingUtil.java
index 1fbb217..5d174e2 100644
--- a/src/main/java/com/project/ding/utils/DingUtil.java
+++ b/src/main/java/com/project/ding/utils/DingUtil.java
@@ -16,9 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import java.util.stream.Collectors;
@Component
@@ -39,6 +37,12 @@ public class DingUtil {
return res;
}
+
+ public String getUserIdByCode(String code) {
+ return Try.of(() -> dtService.getOauth2Service().getUserInfo(code).getUserId())
+ .getOrElse("");
+ }
+
public List getAllDingUserDTO() throws Exception {
List list = getAllDepartment();
@@ -81,6 +85,19 @@ public class DingUtil {
return u1;
}
+
+ public UserDTO getUserById(String id) {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("userid" , id);
+ String url = dtService.getDtConfigStorage().getApiUrl("/topapi/v2/user/get");
+ String responseContent = Try.of(() -> dtService.post(url, jsonObject)).getOrElse("");
+ DingUserDTO dingUserDTO = Try.of(() -> new ObjectMapper().convertValue(
+ JsonPath.read(responseContent, "$.result"),
+ new TypeReference() {
+ })).getOrNull();
+
+ return UserDTO.fromDingUserDTO(dingUserDTO);
+ }
public List getUserIdInDepartment(Long id) throws Exception {
JsonObject jsonObject = new JsonObject();
diff --git a/src/main/java/com/project/ding/utils/JwtUtils.java b/src/main/java/com/project/ding/utils/JwtUtils.java
new file mode 100644
index 0000000..d1757e3
--- /dev/null
+++ b/src/main/java/com/project/ding/utils/JwtUtils.java
@@ -0,0 +1,29 @@
+package com.project.ding.utils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+import org.springframework.stereotype.Component;
+
+import java.security.Key;
+import java.util.Date;
+import java.util.List;
+
+@Component
+public class JwtUtils {
+ private static final Key KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
+ private static final long EXPIRE = 86400000; // 24小时
+
+ public String createToken(String userId, List roles) {
+ return Jwts.builder()
+ .setSubject(userId)
+ .claim("roles", roles)
+ .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
+ .signWith(KEY).compact();
+ }
+
+ public Claims parseToken(String token) {
+ return Jwts.parserBuilder().setSigningKey(KEY).build().parseClaimsJws(token).getBody();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/information/application/InformationApplicationService.java b/src/main/java/com/project/information/application/InformationApplicationService.java
new file mode 100644
index 0000000..fd0f36e
--- /dev/null
+++ b/src/main/java/com/project/information/application/InformationApplicationService.java
@@ -0,0 +1,14 @@
+package com.project.information.application;
+
+import com.project.base.domain.result.Result;
+import com.project.information.domain.param.FileCheckItem;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+public interface InformationApplicationService {
+ Result checkDuplicates(Long subLineId, List files) throws Exception;
+
+
+ Result batchUploadAndOverwrite(MultipartFile[] files, Long subLineId) throws Exception;
+}
diff --git a/src/main/java/com/project/information/application/impl/InformationApplicationServiceImpl.java b/src/main/java/com/project/information/application/impl/InformationApplicationServiceImpl.java
new file mode 100644
index 0000000..ab6d93c
--- /dev/null
+++ b/src/main/java/com/project/information/application/impl/InformationApplicationServiceImpl.java
@@ -0,0 +1,28 @@
+package com.project.information.application.impl;
+
+import com.project.base.domain.result.Result;
+import com.project.information.application.InformationApplicationService;
+import com.project.information.domain.param.FileCheckItem;
+import com.project.information.domain.service.UploadInformationDomainService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+@Service
+public class InformationApplicationServiceImpl implements InformationApplicationService {
+
+ @Autowired
+ private UploadInformationDomainService uploadInformationDomainService;
+
+ @Override
+ public Result checkDuplicates(Long subLineId, List files) throws Exception {
+ return uploadInformationDomainService.checkDuplicates(subLineId , files);
+ }
+
+ @Override
+ public Result batchUploadAndOverwrite(MultipartFile[] files, Long subLineId) throws Exception {
+ return uploadInformationDomainService.batchUploadAndOverwrite(files , subLineId);
+ }
+}
diff --git a/src/main/java/com/project/information/controller/BusinessGroupController.java b/src/main/java/com/project/information/controller/BusinessGroupController.java
index 372bf8b..eda430b 100644
--- a/src/main/java/com/project/information/controller/BusinessGroupController.java
+++ b/src/main/java/com/project/information/controller/BusinessGroupController.java
@@ -16,7 +16,7 @@ import java.util.List;
@RestController
@Slf4j
-@RequestMapping("/businessGroup")
+@RequestMapping("/api/admin/businessGroup")
public class BusinessGroupController {
@Autowired
private BusinessGroupApplicationService businessGroupApplicationService;
diff --git a/src/main/java/com/project/information/controller/InformationController.java b/src/main/java/com/project/information/controller/InformationController.java
index 72b9066..4d84e60 100644
--- a/src/main/java/com/project/information/controller/InformationController.java
+++ b/src/main/java/com/project/information/controller/InformationController.java
@@ -1,13 +1,34 @@
package com.project.information.controller;
+import com.project.base.domain.result.Result;
+import com.project.information.application.InformationApplicationService;
+import com.project.information.application.ProductLineApplicationService;
+import com.project.information.domain.dto.ProductLineDTO;
+import com.project.information.domain.param.CheckDuplicatesParam;
+import com.project.information.domain.param.FileCheckItem;
+import com.project.information.domain.param.ProductLineParam;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
@RestController
@Slf4j
-@RequestMapping("/information")
+@RequestMapping("/api/admin/information")
public class InformationController {
+ @Autowired
+ private InformationApplicationService informationApplicationService;
+
+ @PostMapping("/checkDuplicates")
+ public Result checkDuplicates(CheckDuplicatesParam param) throws Exception {
+ return informationApplicationService.checkDuplicates(param.getSubLineId(), param.getFileList());
+ }
+ @PostMapping("/batchUploadAndOverwrite")
+ public Result batchUploadAndOverwrite(MultipartFile[] files, Long subLineId) throws Exception {
+ return informationApplicationService.batchUploadAndOverwrite(files , subLineId);
+ }
}
diff --git a/src/main/java/com/project/information/controller/ProductLineController.java b/src/main/java/com/project/information/controller/ProductLineController.java
index 11f1426..5867371 100644
--- a/src/main/java/com/project/information/controller/ProductLineController.java
+++ b/src/main/java/com/project/information/controller/ProductLineController.java
@@ -17,7 +17,7 @@ import java.util.List;
@RestController
@Slf4j
-@RequestMapping("/productLine")
+@RequestMapping("/api/admin/productLine")
public class ProductLineController {
@Autowired
diff --git a/src/main/java/com/project/information/domain/dto/BusinessGroupDTO.java b/src/main/java/com/project/information/domain/dto/BusinessGroupDTO.java
index f2bd3fb..722533b 100644
--- a/src/main/java/com/project/information/domain/dto/BusinessGroupDTO.java
+++ b/src/main/java/com/project/information/domain/dto/BusinessGroupDTO.java
@@ -5,6 +5,8 @@ import lombok.Data;
@Data
public class BusinessGroupDTO extends BaseDTO {
+ private Long id;
+
private String name;
private String sort;
}
diff --git a/src/main/java/com/project/information/domain/dto/InformationDTO.java b/src/main/java/com/project/information/domain/dto/InformationDTO.java
index ed98ea0..0c69fe9 100644
--- a/src/main/java/com/project/information/domain/dto/InformationDTO.java
+++ b/src/main/java/com/project/information/domain/dto/InformationDTO.java
@@ -5,13 +5,13 @@ import lombok.Data;
@Data
public class InformationDTO extends BaseDTO {
+ private Long id;
+
private Long subLineId;
private String subLineName;
private String name;
private String filePath;
private Integer parseStatus;
- private Integer partCount;
- private String uploadId;
}
diff --git a/src/main/java/com/project/information/domain/dto/ProductLineDTO.java b/src/main/java/com/project/information/domain/dto/ProductLineDTO.java
index 7de7023..4524cfc 100644
--- a/src/main/java/com/project/information/domain/dto/ProductLineDTO.java
+++ b/src/main/java/com/project/information/domain/dto/ProductLineDTO.java
@@ -8,6 +8,8 @@ import java.util.List;
@Data
public class ProductLineDTO extends BaseDTO {
+ private Long id;
+
private String name;
private Integer sort = 0;
private Boolean leaf;
diff --git a/src/main/java/com/project/information/domain/entity/BusinessGroupEntity.java b/src/main/java/com/project/information/domain/entity/BusinessGroupEntity.java
index 65b1d3b..b5a9ff9 100644
--- a/src/main/java/com/project/information/domain/entity/BusinessGroupEntity.java
+++ b/src/main/java/com/project/information/domain/entity/BusinessGroupEntity.java
@@ -1,12 +1,11 @@
package com.project.information.domain.entity;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.project.base.domain.entity.BaseEntity;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Index;
-import jakarta.persistence.Table;
+import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Comment;
@@ -17,6 +16,9 @@ import org.hibernate.annotations.Comment;
@TableName(value = "evaluator_business_group")
@EqualsAndHashCode(callSuper = true)
public class BusinessGroupEntity extends BaseEntity {
+ @TableId(value = "id" , type = IdType.ASSIGN_ID)
+ @Id
+ private Long id;
@Column(name = "name" , columnDefinition="varchar(255) comment '事业部名称'")
private String name;
diff --git a/src/main/java/com/project/information/domain/entity/InformationEntity.java b/src/main/java/com/project/information/domain/entity/InformationEntity.java
index 2b348c5..f95571a 100644
--- a/src/main/java/com/project/information/domain/entity/InformationEntity.java
+++ b/src/main/java/com/project/information/domain/entity/InformationEntity.java
@@ -1,10 +1,13 @@
package com.project.information.domain.entity;
+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.project.base.domain.entity.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -16,6 +19,9 @@ import org.hibernate.annotations.Comment;
@TableName(value = "evaluator_information")
@EqualsAndHashCode(callSuper = true)
public class InformationEntity extends BaseEntity {
+ @TableId(value = "id" , type = IdType.ASSIGN_ID)
+ @Id
+ private Long id;
@Column(name = "sub_line_id")
@Comment("所属子产品线ID")
@TableField("sub_line_id")
@@ -39,6 +45,6 @@ public class InformationEntity extends BaseEntity {
@Column(name = "parse_status")
@TableField("parse_status")
- @Comment("排序权重")
+ @Comment("文件处理状态")
private Integer parseStatus;
}
diff --git a/src/main/java/com/project/information/domain/entity/ProductLineEntity.java b/src/main/java/com/project/information/domain/entity/ProductLineEntity.java
index 17e8a3f..e294f2f 100644
--- a/src/main/java/com/project/information/domain/entity/ProductLineEntity.java
+++ b/src/main/java/com/project/information/domain/entity/ProductLineEntity.java
@@ -1,13 +1,12 @@
package com.project.information.domain.entity;
+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.project.base.domain.entity.BaseEntity;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Index;
-import jakarta.persistence.Table;
+import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Comment;
@@ -18,6 +17,10 @@ import org.hibernate.annotations.Comment;
@TableName(value = "evaluator_product_line")
@EqualsAndHashCode(callSuper = true)
public class ProductLineEntity extends BaseEntity {
+ @TableId(value = "id" , type = IdType.ASSIGN_ID)
+ @Id
+ private Long id;
+
@Column(name = "name" , columnDefinition="varchar(550) comment '部门名称'")
private String name;
diff --git a/src/main/java/com/project/information/domain/enums/InformationParseStatusEnum.java b/src/main/java/com/project/information/domain/enums/InformationParseStatusEnum.java
new file mode 100644
index 0000000..75ba8a8
--- /dev/null
+++ b/src/main/java/com/project/information/domain/enums/InformationParseStatusEnum.java
@@ -0,0 +1,15 @@
+package com.project.information.domain.enums;
+
+import com.project.base.domain.enums.HasValueEnum;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public enum InformationParseStatusEnum implements HasValueEnum {
+ NotStart(0) ,
+ InProgress(1) ,
+ Success(2) ,
+ Fail(-1);
+ @Getter
+ private final Integer value;
+}
diff --git a/src/main/java/com/project/information/domain/param/BatchCheckParam.java b/src/main/java/com/project/information/domain/param/BatchCheckParam.java
new file mode 100644
index 0000000..7c8eb6a
--- /dev/null
+++ b/src/main/java/com/project/information/domain/param/BatchCheckParam.java
@@ -0,0 +1,11 @@
+package com.project.information.domain.param;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class BatchCheckParam {
+ private Long productLineId;
+ private List files;
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/information/domain/param/CheckDuplicatesParam.java b/src/main/java/com/project/information/domain/param/CheckDuplicatesParam.java
new file mode 100644
index 0000000..c7b66d9
--- /dev/null
+++ b/src/main/java/com/project/information/domain/param/CheckDuplicatesParam.java
@@ -0,0 +1,32 @@
+package com.project.information.domain.param;
+
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.project.base.domain.exception.BusinessErrorException;
+import lombok.Data;
+import org.springframework.util.StringUtils;
+
+import java.util.Collections;
+import java.util.List;
+
+@Data
+public class CheckDuplicatesParam {
+ private Long subLineId;
+ private String files; // 先接收为字符串
+
+ // 手动解析为List的方法
+ public List getFileList() {
+ if (StringUtils.isEmpty(files)) {
+ return Collections.emptyList();
+ }
+ try {
+ return new ObjectMapper().readValue(
+ files,
+ new TypeReference>() {});
+ } catch (JsonProcessingException e) {
+ // 处理解析异常
+ throw new BusinessErrorException("Json解析失败");
+ }
+ }}
diff --git a/src/main/java/com/project/information/domain/param/FileCheckItem.java b/src/main/java/com/project/information/domain/param/FileCheckItem.java
new file mode 100644
index 0000000..8be7b58
--- /dev/null
+++ b/src/main/java/com/project/information/domain/param/FileCheckItem.java
@@ -0,0 +1,9 @@
+package com.project.information.domain.param;
+
+import lombok.Data;
+
+@Data
+public class FileCheckItem {
+ private String name;
+ private Long size; // 单位:Byte
+}
\ No newline at end of file
diff --git a/src/main/java/com/project/information/domain/service/UploadInformationDomainService.java b/src/main/java/com/project/information/domain/service/UploadInformationDomainService.java
index 4754cb7..ff376f5 100644
--- a/src/main/java/com/project/information/domain/service/UploadInformationDomainService.java
+++ b/src/main/java/com/project/information/domain/service/UploadInformationDomainService.java
@@ -1,11 +1,14 @@
package com.project.information.domain.service;
import com.project.base.domain.result.Result;
-import com.project.information.domain.dto.FileDTO;
-import com.project.information.domain.dto.InformationDTO;
-import com.project.information.domain.dto.InitMultipartDTO;
+import com.project.information.domain.param.FileCheckItem;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
public interface UploadInformationDomainService {
+ Result checkDuplicates(Long subLineId, List files) throws Exception;
+
- Result initMultiPartUpload(InformationDTO dto) throws Exception;
+ Result batchUploadAndOverwrite(MultipartFile[] files, Long subLineId) throws Exception;
}
diff --git a/src/main/java/com/project/information/domain/service/UploadService.java b/src/main/java/com/project/information/domain/service/UploadService.java
deleted file mode 100644
index d00e743..0000000
--- a/src/main/java/com/project/information/domain/service/UploadService.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.project.information.domain.service;
-
-import com.project.information.domain.dto.StreamInfoDTO;
-
-import java.util.Map;
-
-public interface UploadService {
-
- /**
- * 分片上传初始化
- *
- * @param path 路径
- * @param filename 文件名
- * @param partCount 分片数量
- * @param contentType /
- * @return /
- */
- Map initMultiPartUpload(String path, String filename, Integer partCount, String contentType);
-
- /**
- * 完成分片上传
- *
- * @param objectName 文件名
- * @param uploadId 标识
- * @return /
- */
- StreamInfoDTO mergeMultipartUpload(String objectName, String uploadId);
-}
\ No newline at end of file
diff --git a/src/main/java/com/project/information/domain/service/impl/UploadInformationDomainServiceImpl.java b/src/main/java/com/project/information/domain/service/impl/UploadInformationDomainServiceImpl.java
index f99172c..50df81d 100644
--- a/src/main/java/com/project/information/domain/service/impl/UploadInformationDomainServiceImpl.java
+++ b/src/main/java/com/project/information/domain/service/impl/UploadInformationDomainServiceImpl.java
@@ -1,56 +1,117 @@
package com.project.information.domain.service.impl;
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.bean.copier.CopyOptions;
-import cn.hutool.core.util.RandomUtil;
-import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.file.FileNameUtil;
+import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.project.base.domain.exception.MissingParameterException;
+import com.project.base.domain.exception.BusinessErrorException;
import com.project.base.domain.result.Result;
-import com.project.base.domain.result.ResultCodeEnum;
-import com.project.information.domain.dto.FileDTO;
import com.project.information.domain.dto.InformationDTO;
-import com.project.information.domain.dto.InitMultipartDTO;
import com.project.information.domain.entity.InformationEntity;
+import com.project.information.domain.entity.ProductLineEntity;
+import com.project.information.domain.enums.InformationParseStatusEnum;
+import com.project.information.domain.param.FileCheckItem;
import com.project.information.domain.service.InformationBaseService;
+import com.project.information.domain.service.ProductLineBaseService;
import com.project.information.domain.service.UploadInformationDomainService;
-import com.project.information.domain.service.UploadService;
+import com.project.information.utils.MinIoUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
import java.util.Objects;
@Service
public class UploadInformationDomainServiceImpl implements UploadInformationDomainService {
- @Autowired
- private UploadService uploadService;
-
@Autowired
private InformationBaseService informationBaseService;
+ @Autowired
+ private ProductLineBaseService productLineBaseService;
+ @Autowired
+ private MinIoUtils minIoUtils;
-
+ private static final long MAX_SIZE = 50 * 1024 * 1024; // 50MB 限制
@Override
- public Result initMultiPartUpload(InformationDTO dto) throws Exception {
- if (StrUtil.isBlank(dto.getName()) || Objects.isNull(dto.getPartCount()) || Objects.isNull(dto.getSubLineId())) {
- throw new MissingParameterException(ResultCodeEnum.MISSING_PARAMETER.getMessage());
+ public Result checkDuplicates(Long subLineId, List files) throws Exception {
+ if (Objects.isNull(subLineId)) {
+ throw new BusinessErrorException("所属子产品线id不能为空");
+ }
+ if (CollUtil.isEmpty(files)) {
+ throw new BusinessErrorException("上传文件集合不能为空");
+ }
+ ProductLineEntity productLine = productLineBaseService.getById(subLineId);
+ if (Objects.isNull(productLine) || !productLine.getLeaf()) {
+ throw new BusinessErrorException("不存在的子产品线");
}
- String path = dto.getSubLineId().toString();
+ List overSizeList = files.stream()
+ .filter(f -> f.getSize() > MAX_SIZE)
+ .map(FileCheckItem::getName)
+ .toList();
- LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(InformationEntity::getSubLineId , dto.getSubLineId());
- queryWrapper.eq(InformationEntity::getName , dto.getName());
- InformationEntity existOne = informationBaseService.getOne(queryWrapper);
- if (Objects.nonNull(existOne)) {
+ if (CollUtil.isNotEmpty(overSizeList)) {
+ // 发现超大文件,直接中断,不查数据库
+ throw new BusinessErrorException(String.format(
+ "以下文件超过50MB限制,禁止上传:【%s】",
+ String.join("、", overSizeList)));
+ }
+ List fileNames = files.stream().map(FileCheckItem::getName).toList();
+ List duplicatesList = informationBaseService.list(new LambdaQueryWrapper()
+ .select(InformationEntity::getName)
+ .eq(InformationEntity::getSubLineId, subLineId)
+ .in(InformationEntity::getName, fileNames))
+ .stream()
+ .map(InformationEntity::getName)
+ .toList();
+ if (CollUtil.isNotEmpty(duplicatesList)) {
+ throw new BusinessErrorException(String.format("你上传的文件集合中【%s】已存在,会进行覆盖操作,是否继续?" , String.join("," , duplicatesList)));
}
+ return Result.success("校验成功");
+ }
- Map stringObjectMap = uploadService.initMultiPartUpload(path, dto.getName() , dto.getPartCount() , "application/octet-stream");
- InitMultipartDTO initMultipartDTO = BeanUtil.mapToBean(stringObjectMap, InitMultipartDTO.class, true, CopyOptions.create());
- initMultipartDTO.setObjectName(path + "/" + dto.getName());
- return Result.success(initMultipartDTO);
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Result batchUploadAndOverwrite(MultipartFile[] files, Long subLineId) throws Exception {
+ if (Objects.isNull(subLineId)) {
+ throw new BusinessErrorException("所属子产品线id不能为空");
+ }
+ if (Objects.isNull(files) || files.length == 0) {
+ throw new BusinessErrorException("上传文件集合不能为空");
+ }
+ ProductLineEntity productLine = productLineBaseService.getById(subLineId);
+ if (Objects.isNull(productLine) || !productLine.getLeaf()) {
+ throw new BusinessErrorException("不存在的子产品线");
+ }
+ List successFiles = new ArrayList<>();
+ for (MultipartFile file : files) {
+ String fileName = file.getOriginalFilename();
+ // 删掉原来的
+ informationBaseService.remove(new LambdaQueryWrapper()
+ .eq(InformationEntity::getSubLineId, subLineId)
+ .eq(InformationEntity::getName, fileName));
+ String filePath = String.format("%s/%s/%s.%s",
+ subLineId,
+ DateUtil.format(new Date(), "yyyyMMdd"),
+ IdUtil.fastSimpleUUID(),
+ FileNameUtil.extName(fileName));
+ minIoUtils.uploadFile(file.getInputStream() , filePath);
+ successFiles.add(fileName);
+ InformationDTO informationDTO = new InformationDTO();
+ informationDTO.setSubLineId(subLineId);
+ informationDTO.setSubLineName(productLine.getName());
+ informationDTO.setName(fileName);
+ informationDTO.setFilePath(filePath);
+ informationDTO.setParseStatus(InformationParseStatusEnum.NotStart.getValue());
+ informationBaseService.save(informationDTO.toEntity(InformationEntity::new));
+ }
+ return Result.success(String.format("上传成功:【%s】" , String.join("," , successFiles)));
}
}
diff --git a/src/main/java/com/project/information/domain/service/impl/UploadServiceImpl.java b/src/main/java/com/project/information/domain/service/impl/UploadServiceImpl.java
deleted file mode 100644
index 85e641b..0000000
--- a/src/main/java/com/project/information/domain/service/impl/UploadServiceImpl.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.project.information.domain.service.impl;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.project.information.domain.dto.StreamInfoDTO;
-import com.project.information.domain.service.UploadService;
-import com.project.information.utils.MinIoUtils;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-
-import java.util.Map;
-
-@Service
-@RequiredArgsConstructor
-public class UploadServiceImpl implements UploadService {
-
- private final MinIoUtils minIoUtils;
-
- @Override
- public Map initMultiPartUpload(String path, String filename, Integer partCount, String contentType) {
- path = path.replaceAll("/+", "/");
- if (path.indexOf("/") == 0) {
- path = path.substring(1);
- }
- String filePath = path + "/" + filename;
-
- Map result;
- // 单文件,直接上传
- if (partCount == 1) {
- String uploadObjectUrl = minIoUtils.getUploadObjectUrl(filePath);
- result = ImmutableMap.of("uploadUrls", ImmutableList.of(uploadObjectUrl));
- } else {//多文件,分片上传
- result = minIoUtils.initMultiPartUpload(filePath, partCount, contentType);
- }
-
- return result;
- }
-
- @Override
- public StreamInfoDTO mergeMultipartUpload(String objectName, String uploadId) {
- return minIoUtils.mergeMultipartUpload(objectName, uploadId);
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/project/information/utils/MinIoUtils.java b/src/main/java/com/project/information/utils/MinIoUtils.java
index fa37d12..982f43a 100644
--- a/src/main/java/com/project/information/utils/MinIoUtils.java
+++ b/src/main/java/com/project/information/utils/MinIoUtils.java
@@ -5,18 +5,15 @@ import com.google.common.collect.HashMultimap;
import com.project.information.config.CustomMinioClient;
import com.project.information.config.MinioProperties;
import com.project.information.domain.dto.StreamInfoDTO;
-import io.minio.GetObjectArgs;
-import io.minio.GetPresignedObjectUrlArgs;
-import io.minio.ListPartsResponse;
-import io.minio.MinioClient;
+import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Part;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
-import org.springframework.stereotype.Component;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -124,4 +121,83 @@ public class MinIoUtils {
return null;
}
}
+ /**
+ * 简化的InputStream上传方法(自动设置ContentType)
+ *
+ * @param inputStream 文件输入流
+ * @param fileName 文件名
+ * @return
+ */
+ public String uploadFile(InputStream inputStream, String fileName) throws Exception{
+ String contentType = getContentTypeByFileName(fileName);
+ uploadInputStream(inputStream, fileName, contentType);
+ return fileName;
+ }
+
+ /**
+ * 根据文件扩展名自动设置ContentType
+ *
+ * @param fileName 文件名
+ * @return ContentType
+ */
+ private String getContentTypeByFileName(String fileName) {
+ if (StrUtil.isBlank(fileName)) {
+ return "application/octet-stream";
+ }
+
+ String extension = fileName.toLowerCase();
+ if (extension.endsWith(".xlsx")) {
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+ } else if (extension.endsWith(".xls")) {
+ return "application/vnd.ms-excel";
+ } else if (extension.endsWith(".docx")) {
+ return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
+ } else if (extension.endsWith(".doc")) {
+ return "application/msword";
+ } else if (extension.endsWith(".pdf")) {
+ return "application/pdf";
+ } else if (extension.endsWith(".txt")) {
+ return "text/plain";
+ } else if (extension.endsWith(".jpg") || extension.endsWith(".jpeg")) {
+ return "image/jpeg";
+ } else if (extension.endsWith(".png")) {
+ return "image/png";
+ } else {
+ return "application/octet-stream";
+ }
+ }
+
+ public void uploadInputStream(InputStream inputStream, String objectName, String contentType) throws Exception{
+ if (inputStream == null) {
+ log.error("文件上传失败,输入流不能为空");
+ return;
+ }
+ if (StrUtil.isBlank(objectName)) {
+ log.error("文件上传失败,文件名不能为空");
+ return;
+ }
+
+ // 设置默认内容类型
+ if (StrUtil.isBlank(contentType)) {
+ contentType = "application/octet-stream";
+ }
+
+ // 设置请求头
+ HashMultimap headers = HashMultimap.create();
+ headers.put("Content-Type", contentType);
+
+ // 上传文件
+ customMinioClient.putObject(
+ PutObjectArgs.builder()
+ .bucket(minioProperties.getBucket())
+ .object(objectName)
+ .stream(inputStream, -1, 10485760) // 10MB part size
+ .headers(headers)
+ .build()
+ );
+
+ log.info("文件上传成功,"+objectName);
+
+
+ }
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index b683466..5d81f67 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -10,11 +10,14 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver
password: Itc@123456
url: jdbc:mysql://172.16.204.50:3306/ai_evaluator?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
+# url: jdbc:mysql://8.129.84.155:3306/ai_evaluator?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
username: root
# Redis
data:
redis:
host: 172.16.204.50
+
+# host: 8.129.84.155
port: 6379
password: 123456
database: 3
@@ -47,10 +50,10 @@ spring:
generator:
write-numbers-as-strings: true
minio:
- endpoint: ${MINIO_ENDPOINT:http://172.16.124.14:9000}
- accessKey: ${MINIO_ASSESSKEY:X42cCp42U4BSJHPAV1Aq}
- secretKey: ${MINIO_SECRETKEY:099NCOOSklhJyeJE6C73YxbbUT4Y4dRJK0RafAgv}
- bucket: ${MINIO_BUCKET:proposalworkshop}
+ endpoint: ${MINIO_ENDPOINT:http://172.16.204.50:9000}
+ accessKey: ${MINIO_ASSESSKEY:DTKYZDZM1i31XOvd24SP}
+ secretKey: ${MINIO_SECRETKEY:PnfLPcJbvaUboZIwYZAADPB0pDtPZgbi0QiLSs3C}
+ bucket: ${MINIO_BUCKET:ai-evaluator}
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
@@ -59,4 +62,8 @@ mybatis-plus:
milvus:
host: 172.16.204.50
- port: 19530
\ No newline at end of file
+ port: 19530
+ding:
+ appKey: ding7ocyghkjyowg38ck
+ appSecret: x1SWUdGfiLzjeRAoicGQO49J73sI59gwgPXLLkcoI6DY4M0UZDDND4QT2laNtT22
+ agentId: 4145005949
\ No newline at end of file