提交 5cb7fb90 作者: duanxincheng

cmeeting后台登录鉴权,会议修改纪要调优,生成的纪要文件内容格式调优

父级 0db0455d
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
<maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version> <mybatis-plus.version>3.3.0</mybatis-plus.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
...@@ -328,6 +328,35 @@ ...@@ -328,6 +328,35 @@
<artifactId>spring-boot-starter-data-redis</artifactId> <artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.0</version> <!-- 与你的Spring Boot版本一致 --> <version>2.7.0</version> <!-- 与你的Spring Boot版本一致 -->
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.7.0</version>
</dependency>
<!--security核心包-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.7.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
package com.cmeeting.ad.config;
import com.cmeeting.ad.service.CustomUserDetailsService;
import org.springframework.security.authentication.*;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Assert;
import java.util.Map;
/**
* An {@link AuthenticationProvider} implementation that retrieves user details from a
* {@link UserDetailsService}.
*
* @author Ben Alex
* @author Rob Winch zw
* 此方法从DaoAuthenticationProvider中复制过来 因为现在有两种登录方式,一种是原来的登录方式,另一种是手机登录的
* 重写 retrieveUser方法
*/
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
// ~ Static fields/initializers
// =====================================================================================
/**
* The plaintext password used to perform
* PasswordEncoder#matches(CharSequence, String)} on when the user is
* not found to avoid SEC-2056.
*/
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
// ~ Instance fields
// ================================================================================================
private PasswordEncoder passwordEncoder;
/**
* The password used to perform
* {@link PasswordEncoder#matches(CharSequence, String)} on when the user is
* not found to avoid SEC-2056. This is necessary, because some
* {@link PasswordEncoder} implementations will short circuit if the password is not
* in a valid format.
*/
private volatile String userNotFoundEncodedPassword;
private CustomUserDetailsService customUserDetailsService;
private UserDetailsPasswordService userDetailsPasswordService;
public CustomAuthenticationProvider() {
setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
}
// ~ Methods
// ========================================================================================================
@SuppressWarnings("deprecation")
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String presentedPassword = authentication.getCredentials().toString();
// 密码验证在login接口做过了
// if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
// logger.debug("Authentication failed: password does not match stored value");
// throw new BadCredentialsException(messages.getMessage(
// "AbstractUserDetailsAuthenticationProvider.badCredentials",
// "Bad credentials"));
// }
}
protected void doAfterPropertiesSet() {
Assert.notNull(this.customUserDetailsService, "A customUserDetailsService must be set");
}
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
prepareTimingAttackProtection();
Map<String, String> details = (Map<String, String>) authentication.getDetails();
try {
String uid = details.get("userId");
String tenantId = details.get("tenantId");
UserDetails loadedUser = this.getCustomUserDetailsService().loadUser(uid, tenantId);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"customUserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
} catch (UsernameNotFoundException ex) {
mitigateAgainstTimingAttack(authentication);
throw ex;
} catch (InternalAuthenticationServiceException ex) {
throw ex;
} catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
@Override
protected Authentication createSuccessAuthentication(Object principal,
Authentication authentication, UserDetails user) {
boolean upgradeEncoding = this.userDetailsPasswordService != null
&& this.passwordEncoder.upgradeEncoding(user.getPassword());
if (upgradeEncoding) {
String presentedPassword = authentication.getCredentials().toString();
String newPassword = this.passwordEncoder.encode(presentedPassword);
user = this.userDetailsPasswordService.updatePassword(user, newPassword);
}
return super.createSuccessAuthentication(principal, authentication, user);
}
private void prepareTimingAttackProtection() {
if (this.userNotFoundEncodedPassword == null) {
this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
}
}
private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString();
this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword);
}
}
/**
* Sets the PasswordEncoder instance to be used to encode and validate passwords. If
* not set, the password will be compared using {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}
*
* @param passwordEncoder must be an instance of one of the {@code PasswordEncoder}
* types.
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
this.passwordEncoder = passwordEncoder;
this.userNotFoundEncodedPassword = null;
}
protected PasswordEncoder getPasswordEncoder() {
return passwordEncoder;
}
public CustomUserDetailsService getCustomUserDetailsService() {
return customUserDetailsService;
}
public void setCustomUserDetailsService(CustomUserDetailsService customUserDetailsService) {
this.customUserDetailsService = customUserDetailsService;
}
public void setUserDetailsPasswordService(
UserDetailsPasswordService userDetailsPasswordService) {
this.userDetailsPasswordService = userDetailsPasswordService;
}
}
package com.cmeeting.ad.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @author liuzhenmeng
* @date 2024/2/4 17:05
*/
@Data
@Component
@ConfigurationProperties(prefix = "robot.custom")
public class CustomWhiteConfigProperties {
/**
* 接口白名单
*/
private List<String> whiteTables = new ArrayList<>();
}
package com.cmeeting.ad.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
@Configuration
public class LdapConfig {
@Value("${ldap.url}")
private String ldapUrl;
@Value("${ldap.base}")
private String ldapBase;
@Value("${ldap.adName}")
private String ldapUserDn;
@Value("${ldap.adPwd}")
private String ldapUserPwd;
@Value("${ldap.referral}")
private String ldapReferral;
/*
* SpringLdap的javaConfig注入方式
*/
@Bean
public LdapTemplate ldapTemplate() {
return new LdapTemplate(contextSourceTarget());
}
/*
* SpringLdap的javaConfig注入方式
*/
@Bean
public LdapContextSource contextSourceTarget() {
LdapContextSource ldapContextSource = new LdapContextSource();
ldapContextSource.setUrl(ldapUrl);
ldapContextSource.setBase(ldapBase);
ldapContextSource.setUserDn(ldapUserDn);
ldapContextSource.setPassword(ldapUserPwd);
ldapContextSource.setReferral(ldapReferral);
return ldapContextSource;
}
}
package com.cmeeting.ad.config;
import cn.hutool.core.util.ArrayUtil;
import com.cmeeting.ad.filter.RobotJwtAuthenticationTokenFilter;
import com.cmeeting.ad.service.CustomUserDetailsService;
import com.cmeeting.ad.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.annotation.Resource;
import java.util.List;
/**
* @Classname WebSecurityConfig
* @Description Security配置类
* @Author robot
* @Date 2019-05-07 09:10
* @Version 1.0
*/
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class RobotWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RobotJwtAuthenticationTokenFilter robotJwtAuthenticationTokenFilter;
// @Resource
// private RobotJwtCustomTokenFilter robotJwtCustomTokenFilter;
@Autowired
private CustomWhiteConfigProperties customWhiteConfigProperties;
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private RobotWhiteConfigProperties configProperties;
/**
* 解决 无法直接注入 AuthenticationManager
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean(name = "customAuthenticationProvider")
public AuthenticationProvider customAuthenticationProvider() {
CustomAuthenticationProvider customAuthenticationProvider = new CustomAuthenticationProvider();
customAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
customAuthenticationProvider.setHideUserNotFoundExceptions(false);
customAuthenticationProvider.setCustomUserDetailsService(customUserDetailsService);
return customAuthenticationProvider;
}
@Bean(name = "bCryptPasswordEncoder")
public BCryptPasswordEncoder bCryptPasswordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
/**
* 配置策略
*
* @param httpSecurity
* @throws Exception
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
List<String> whiteTables = configProperties.getWhiteTables();
String[] api = ArrayUtil.toArray(whiteTables, String.class);
List<String> customWhiteTables = customWhiteConfigProperties.getWhiteTables();
String[] customApi = ArrayUtil.toArray(customWhiteTables, String.class);
httpSecurity
// 由于使用的是JWT,我们这里不需要csrf
.csrf().disable()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
// 对于登录login 图标 要允许匿名访问
.antMatchers(customApi).access("@robotJwtCustomTokenFilter.checkToken(request)")
.antMatchers(api).anonymous()
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css",
"/**/*.js", "/**/*.map")
.permitAll()
.antMatchers("/error").anonymous()
.antMatchers("/user/**").anonymous()
.antMatchers("/web/**").anonymous()
.antMatchers("/file/**")
.permitAll()
// 访问/user 需要拥有admin权限
// .antMatchers("/user").hasAuthority("ROLE_ADMIN")
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
// 添加自定义异常入口
// httpSecurity
// .exceptionHandling()
// .authenticationEntryPoint(new RobotAuthenticationEntryPointImpl())
// .accessDeniedHandler(new RobotAccessDeniedHandler());
// 添加JWT filter 用户名登录
httpSecurity
// 添加JWT验证过滤器
.addFilterBefore(robotJwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
// auth.userDetailsService(mobileUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
auth.authenticationProvider(customAuthenticationProvider());
}
}
package com.cmeeting.ad.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @author 张伟
* @date 2021/2/2 17:47
*/
@Data
@Component
@ConfigurationProperties(prefix = "robot.white")
public class RobotWhiteConfigProperties {
/**
* 接口白名单
*/
private List<String> whiteTables = new ArrayList<>();
private List<String> whiteSignUrls = new ArrayList<>();
}
package com.cmeeting.ad.controller;
import com.cmeeting.ad.service.UserService;
import com.cmeeting.ad.vo.UserVo;
import com.cmeeting.util.IPUtils;
import com.cmeeting.util.R;
import com.cmeeting.ad.vo.ApplicationUserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 咨询窗用
*
* @param vo
* @return
*/
@CrossOrigin
@PostMapping("/login")
public R login(@RequestBody @Validated ApplicationUserVO.LoginDecrypt vo, HttpServletRequest request) {
String ipAddr = IPUtils.getIpAddr(request);
return userService.login(vo.getAgentId(), vo.getData(), ipAddr);
}
@PostMapping(value = "/auth")
public R auth(@Validated @RequestBody UserVo.Auth vo) {
return R.ok(userService.auth(vo));
}
/**
* 授权查询接口
* @param search
* @param type
* @param userType
* @param categoryId
* @param targetId
* @param authType
* @return
*/
@GetMapping("/list")
public R list(String search, @RequestParam Integer type, @RequestParam Integer userType,
String categoryId, @RequestParam String targetId, Integer authType) {
return R.ok(userService.list(search, type, userType, categoryId, targetId, authType));
}
}
\ No newline at end of file
package com.cmeeting.ad.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 应用用户配置关联表(ModulePermissions)实体类
*
* @author makejava
* @since 2024-05-11 13:22:54
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("core_module_permissions")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ModulePermissions implements Serializable {
private static final long serialVersionUID = 686579698726893795L;
/**
* 配置表主键
*/
private String id;
/**
* 0 分类 1用户
*/
private Integer type;
/**
* 0自建 1 应用 2通讯录 3超管分享
*/
private Integer userType;
/**
* 0 角色授权 1应用授权 2文档授权
*/
private Integer source;
/**
* 关联id(分类id或用户id)
*/
private String relId;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
* 角色id /应用id /文档id
*/
private String targetId;
private String tenantId;
@TableField(exist = false)
private String name;
@TableField(exist = false)
private String deptName;
}
package com.cmeeting.ad.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 租户平台设置关联表(RelPlatformSettingTenant)实体类
*
* @author makejava
* @since 2023-06-12 10:03:26
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("rel_platform_setting_tenant")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RelPlatformSettingTenant implements Serializable {
private static final long serialVersionUID = -89908876679147553L;
private String id;
private String tenantId;
/**
* 菜单id
*/
private String platformSettingId;
private Integer isDel;
private Date createTime;
private String createUser;
private Date updateTime;
private String updateUser;
}
package com.cmeeting.ad.entity;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* 登录用户信息
*
* @author 王长伟
* @Date 2019-05-07 09:11
**/
@Setter
@Getter
@Accessors(chain = true)
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RobotSecurityUser implements UserDetails {
/**
* 0系统用户 1应用用户 2通信录用户
*/
private Integer userType;
private String id;
private Long userId;
private String username;
@JsonIgnore
private String password;
private String tenantId;
/**
*
* 用户与租户区别字段
*/
private Integer type;
@JsonIgnore
private Collection<? extends GrantedAuthority> authorities;
private RoleTree menus;
private SysPlatformSetting sysPlatformSetting;
private String nick;
private String picture;
private String path;
@JsonIgnore
private boolean accountNonLocked;
@JsonIgnore
private boolean credentialsNonExpired;
private String status;
private String validity;
private Integer reviewStatus;
private Long kfTenantId;
private String nickName;
/**
* 是否是工单客服人员
*/
private Boolean isCustomerOrder;
/**
* 最大接待量
*/
private Integer maxReception;
/**
* 工号
*/
private String jobNo;
/**
* 是否过期
*/
private Boolean overdue;
private String language;
/**
* 返回分配给用户的角色列表
*
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
/**
* 账户是否未过期,过期无法验证
* @return
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
* @return
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 指示是否未过期的用户的凭据(密码),过期的凭据防止认证
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
* @return
*/
@Override
public boolean isEnabled() {
return true;
}
public Boolean getOverdue() {
if (StrUtil.isNotBlank(validity)){
return DateUtil.parseDate(validity).getTime()<DateUtil.date().getTime();
}else{
return false;
}
}
}
package com.cmeeting.ad.entity;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @author: 王长伟
*/
@Getter
@Setter
public class RoleTree implements Serializable {
private static final long serialVersionUID = 1L;
private List<String> tenantCenter = new ArrayList<>();
private List<String> interactCenter = new ArrayList<>();
private List<String> knowledgeCenter = new ArrayList<>();
private List<String> llmCenter = new ArrayList<>();
private List<String> statisticsCenter = new ArrayList<>();
private List<String> securityCenter = new ArrayList<>();
private List<String> robotCenter = new ArrayList<>();
private List<String> staffCenter = new ArrayList<>();
private List<String> processCenter = new ArrayList<>();
}
package com.cmeeting.ad.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* <p>
* 菜单权限表
* </p>
*
* @author robot
* @since 2019-04-21
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_menu")
public class SysMenu extends Model<SysMenu> {
private static final long serialVersionUID = 1L;
/**
* 菜单ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Integer id;
/**
* 菜单名称
*/
private String name;
/**
* 菜单权限标识
*/
private String perms;
/**
* 前端path / 即跳转路由
*/
private String path;
/**
* 菜单组件
*/
private String component;
/**
* 父菜单ID
*/
private Integer parentId;
/**
* 图标
*/
private String icon;
/**
* 是否为外链
*/
private Boolean isAdmin;
/**
* 排序
*/
private Integer sort;
/**
* 菜单类型 (类型 1:目录 2:菜单 3:按钮)
*/
private Integer type;
/**
* 所属大功能模块
*/
private String parentSystem;
/**
* 在租户菜单是否展示 新增
*/
private Integer isShow;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@TableField(fill = FieldFill.INSERT)
@TableLogic
private Integer isDel;
/**
* 非数据库字段
* 父菜单名称
*/
@TableField(exist = false)
private String parentName;
/**
* 非数据库字段
* 菜单等级
*/
@TableField(exist = false)
private Integer level;
/**
* 非数据库字段
* 子菜单
*/
@TableField(exist = false)
private List<SysMenu> children;
@TableField(exist = false)
private String parentPath;
@TableField(exist = false)
private String key;
@TableField(exist = false)
private String title;
public String getTitle() {
return name;
}
@Override
public boolean equals(Object o) {
return Objects.hash(id) == o.hashCode();
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
package com.cmeeting.ad.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 平台设置表(SysPlatformSetting)实体类
*
* @author makejava
* @since 2023-06-12 11:43:45
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_platform_setting")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SysPlatformSetting implements Serializable {
private static final long serialVersionUID = -86441288874834748L;
/**
* 主键id
*/
private String id;
/**
* 是否使用机器人 0:false 1:true, 默认为1
*/
private Integer robot;
/**
* 是否使用数字人 0:false 1:true, 默认为1
*/
private Integer digitalPerson;
/**
* 是否使用客服系统 0:false 1:true ,默认为1
*/
private Integer moduleKefu;
/**
* 是否使用大语言模型 0:false 1:true ,默认为1
*/
private Integer largeLanguage;
/**
* 是否使用工单系统 0:false 1:true , 默认为1
*/
private Integer moduleTicket;
/**
* 是否使用问答知识点 0:false 1:true, 默认为1
*/
private Integer qaKnowledge;
/**
* 是否使用知识导图 0:false 1:true, 默认为1
*/
private Integer atlasKnowledge;
/**
* 是否使用多轮对话 0:false 1:true ,默认为1
*/
private Integer dialogKnowledge;
/**
* 是否使用文档知识点 0:false 1:true , 默认为1
*/
private Integer docKnowledge;
/**
* logo登录标签地址
*/
private String loginLogoUrl;
/**
* logo首页标签地址
*/
private String indexLogoUrl;
/**
* 背景图片地址
*/
private String backgroundUrl;
/**
* 推广图片地址
*/
private String advertiseUrl;
/**
* 底部信息
*/
private String bottomInfo;
/**
* 创建时间;创建时间
*/
private Date createTime;
/**
* 更新时间;更新时间
*/
private Date updateTime;
/**
* 创建人;创建人id
*/
private String createUser;
/**
* 更新人;更新人id
*/
private String updateUser;
/**
* 删除标识;0正常,1删除,默认为0
*/
private Integer isDel;
private Integer isAdmin;
private String agreement;
}
package com.cmeeting.ad.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* 租户表
* </p>
*
* @author robot
* @since 2019-08-10
*/
@Getter
@Setter
public class SysTenant implements Serializable {
/**
* 租户id
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
private String kefuId;
/**
* 租户名称
*/
private String name;
/**
* 删除标记
*/
@TableField(fill = FieldFill.INSERT)
@TableLogic
private Boolean isDel;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private String createUser;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateUser;
/**
* 有效期
*/
private String validity;
/**
* 0纯网页 11企业微信第三方应用 12企业微信自建 21钉钉第三方应用 22钉钉自建 3 teams
*/
// private Integer tenantType;
/**
* 启用状态(0: 启用, 1: 禁用)
*/
private String status;
/**
* 备注
*/
private String remarks;
/**
* 机器人数量
*/
private Integer robotNums;
/**
* 知识点数量上限
*/
private Integer qaNums;
private Integer atlasNums;
private Integer dialogNums;
private Integer docNums;
private Integer formNums;
private Integer userNums;
private Integer regulatorsNums;
/**
* 知创建租户的系统用户名称
*/
private String userName;
/**
* 语言
*/
private String language;
@TableField(exist = false)
private Boolean expired;
}
package com.cmeeting.ad.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 用户同步表(SysUserSync)实体类
*
* @author makejava
* @since 2024-04-25 14:22:40
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_user_sync")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class SysUserSync implements Serializable {
private static final long serialVersionUID = 389470942890145726L;
/**
* 同步用户主键id
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* 用户唯一标识
*/
private String userId;
/**
* 用户姓名
*/
private String name;
/**
* 姓
*/
private String lastName;
/**
* 部门id(用户同步的时候没有给这个值为默认分类)
*/
private String deptId;
/**
* 名
*/
private String firstName;
/**
* 别名
*/
private String nickName;
/**
* 性别 0男 1女
*/
private String gender;
/**
* 手机号
*/
private String phone;
/**
* 个人邮箱
*/
private String email;
/**
* 公司邮箱
*/
private String companyEmail;
/**
* 部门
*/
private String department;
/**
* 分机号
*/
private String extensionNumber;
/**
* 工位
*/
private String workStation;
/**
* 人员类型
*/
private String type;
/**
* 国家
*/
private String country;
/**
* 城市
*/
private String city;
/**
* 地区
*/
private String area;
/**
* 直属上级
*/
private String directlySubordinateSuperior;
/**
* 虚线上级
*/
private String dottedLineSuperior;
/**
* 职务
*/
private String position;
/**
* 职级
*/
private String rank;
/**
* 人员状态
*/
private String state;
/**
* 入职日期
*/
private String entryDate;
/**
* 入职类型
*/
private String entryType;
/**
* 试用期
*/
private String probationPeriod;
/**
* 转正日期
*/
private String probationCompletionDate;
/**
* 转正状态
*/
private String probationCompletionType;
/**
* 出生日期
*/
private String birthDate;
/**
* 籍贯
*/
private String nativePlace;
/**
* 民族
*/
private String nationality;
/**
* 婚姻状况
*/
private String maritalStatus;
/**
* 政治面貌
*/
private String politicalOutlook;
/**
* 首次参加工作日期
*/
private String firstWorkDate;
/**
* 用户信息
*/
private Object allFieldParam;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
/**
* 租户id
*/
private String tenantId;
@TableField(exist = false)
private String path;
@TableField(exist = false)
private String deptName;
}
package com.cmeeting.ad.filter;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.cmeeting.ad.entity.RobotSecurityUser;
import com.cmeeting.ad.util.JwtUtil;
import com.cmeeting.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author 王长伟
* @Description token过滤器来验证token有效性
* @Date 00:32 2019-03-17
* @Param
* @return
**/
@Slf4j
@Component
public class RobotJwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Resource
private JwtUtil jwtUtil;
@Value("${jwt.expireTime}")
private long expireTime;
@Resource
private RedisUtils redisUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String token = jwtUtil.getToken(request);
if (StringUtils.isNotBlank(token)) {
Object o = redisUtil.get(token);
RobotSecurityUser securityUser = JSONObject.parseObject(JSONObject.toJSONString(o), RobotSecurityUser.class);
if (ObjectUtil.isNotNull(securityUser)) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
//延长token时间
redisUtil.expire(token, expireTime);
}
}
chain.doFilter(request, response);
}
}
package com.cmeeting.ad.service;
import org.springframework.security.core.userdetails.UserDetails;
/**
* 写这个方法的目的是为了让业务实现层能够根据类型执行对应的业务逻辑
*/
public interface CustomUserDetailsService {
UserDetails loadUser(String uid, String tenantId);
}
package com.cmeeting.ad.service;
public interface ILdapService {
/**
* LDAP用户认证
*/
boolean authenticate(String loginName, String password);
}
package com.cmeeting.ad.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cmeeting.ad.entity.SysTenant;
/**
* <p>
* 租户表 服务类
* </p>
*
* @author robot
* @since 2019-08-10
*/
public interface ISysTenantService extends IService<SysTenant> {
}
// service/UserService.java
package com.cmeeting.ad.service;
import com.cmeeting.ad.entity.RoleTree;
import com.cmeeting.ad.entity.SysMenu;
import com.cmeeting.ad.vo.UserVo;
import com.cmeeting.util.R;
import java.util.List;
public interface UserService {
R login(String agentId, String data, String ip);
String auth(UserVo.Auth vo);
List<String> getRoleIdByUserId(String tenantId, String userId, String path);
/**
* 获取登录后授权的菜单
* @param sysMenuList
* @return
*/
RoleTree getLoginMenus(List<SysMenu> sysMenuList);
Object list(String search, Integer type, Integer userType, String categoryId, String targetId, Integer authType);
}
\ No newline at end of file
package com.cmeeting.ad.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.cmeeting.ad.entity.RobotSecurityUser;
import com.cmeeting.ad.service.CustomUserDetailsService;
import com.cmeeting.constant.LoginUserConstant;
import com.cmeeting.constant.UserAdminRouteConstant;
import com.cmeeting.util.HttpClientUtils;
import com.cmeeting.util.UserAdminConfig;
import com.cmeeting.util.UserAdminTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 登录实现
*
* @author zw
*/
@Slf4j
@Service
public class DetailsServiceImpl implements CustomUserDetailsService {
@Resource
private UserAdminConfig userAdminConfig;
/**
* @return 用户基本信息
* @throws UsernameNotFoundException 用户不存在
*/
@Override
public UserDetails loadUser(String uid, String tenantId) {
RobotSecurityUser user = new RobotSecurityUser();
user.setUserType(LoginUserConstant.SYS);
String url = userAdminConfig.getUserAdminDomain() + UserAdminRouteConstant.SyncUser.INFO + "?id=" + uid + "&tenantId=" + tenantId;
JSONObject jsonObject = HttpClientUtils.httpGet(url, UserAdminTokenUtil.getUserAdminToken());
JSONObject data = jsonObject.getJSONObject("data");
user.setId(data.getString("userId"));
user.setUsername(data.getString("name"));
user.setNick(data.getString("nickName"));
user.setTenantId(data.getString("tenantId"));
return user;
}
}
package com.cmeeting.ad.service.impl;
import com.cmeeting.ad.service.ILdapService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.naming.directory.DirContext;
@Component
@Slf4j
public class LdapServiceImpl implements ILdapService {
@Resource
private LdapTemplate ldapTemplate;
@Value("${ldap.domain}")
private String ldapDomain;
/**
* LDAP用户认证
*/
@Override
public boolean authenticate(String loginName, String password) {
String userDomainName = loginName + ldapDomain;
DirContext ctx = null;
try {
ctx = ldapTemplate.getContextSource().getContext(userDomainName, password);
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
LdapUtils.closeContext(ctx);
}
return false;
}
}
package com.cmeeting.ad.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmeeting.ad.entity.SysTenant;
import com.cmeeting.mapper.primary.SysTenantMapper;
import com.cmeeting.ad.service.ISysTenantService;
import org.springframework.stereotype.Service;
/**
* <p>
* 租户表 服务实现类
* </p>
*
* @author robot
* @since 2019-08-10
*/
@Service
public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant> implements ISysTenantService {
}
package com.cmeeting.ad.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmeeting.ad.entity.*;
import com.cmeeting.ad.util.SecurityUtil;
import com.cmeeting.ad.vo.UserVo;
import com.cmeeting.mapper.primary.CommonMapper;
import com.cmeeting.ad.service.ILdapService;
import com.cmeeting.ad.service.ISysTenantService;
import com.cmeeting.ad.service.UserService;
import com.cmeeting.ad.util.JwtUtil;
import com.cmeeting.constant.CategoryConstant;
import com.cmeeting.constant.UserAdminRouteConstant;
import com.cmeeting.mapper.secondary.SysUserSysMapper;
import com.cmeeting.util.*;
import com.cmeeting.ad.vo.ApplicationUserVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Value("${aec.key}")
public String aesKey;
@Value("${permission.applicationId}")
public String applicationId;
@Value("${removeMenus}")
private String removeMenus;
@Value("${jwt.expireTime}")
private String expireTime;
@Value("${permission.tenantId}")
public String permissionTenantId;
@Resource
private ILdapService iLdapService;
@Resource
private UserAdminConfig userAdminConfig;
@Resource
private AuthenticationManager authenticationManager;
@Resource
private RedisUtils redisUtils;
@Resource
private ISysTenantService iTenantService;
@Resource
private JwtUtil jwtUtil;
@Resource
private CommonMapper commonMapper;
@Resource
private SysUserSysMapper sysUserSysMapper;
@Override
public R login(String agentId, String data, String ip) {
String mess = AESUtils.decrypt(data, aesKey);
ApplicationUserVO.Login login = JSONObject.parseObject(mess, ApplicationUserVO.Login.class);
log.info("登录信息:{}", login);
return loginByAD(login);
}
@Override
public String auth(UserVo.Auth vo) {
String userId = vo.getId();
String nick = vo.getNick();
HashMap<String, String> stringStringHashMap = new HashMap<>();
SysTenant sysTenant = iTenantService.getById(permissionTenantId);
stringStringHashMap.put("userId", userId);
stringStringHashMap.put("tenantId", permissionTenantId);
stringStringHashMap.put("language", sysTenant.getLanguage());
// iUserLoginInfoService.updateInfo(null, userId, tenantId, null);
stringStringHashMap.put("nick", nick);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userId, permissionTenantId);
usernamePasswordAuthenticationToken.setDetails(stringStringHashMap);
Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
RobotSecurityUser userDetail = (RobotSecurityUser) authentication.getPrincipal();
System.out.println(userId.toString());
String token = jwtUtil.generateToken(userDetail);
List<SysMenu> menuList = commonMapper.getMenuList(permissionTenantId);
// 需要对menuList进行过滤
// 根据uid查询对应角色,然后查询角色对应的菜单权限
// List<String> roleIdList = getRoleIdByUserId(permissionTenantId, userId, path);
// List<SysMenu> menuList1 = CollectionUtils.isEmpty(roleIdList) ? new ArrayList<>() : commonMapper.getMenuByRoleList(roleIdList, permissionTenantId);
// Set<Integer> menuSet = menuList1.stream().map(SysMenu::getId).collect(Collectors.toSet());
// List<SysMenu> filteredMenu = menuList.stream().filter(menu -> menuSet.contains(menu.getId())).collect(Collectors.toList());
// menuList = (List<SysMenu>) CollectionUtils.intersection(filteredMenu, menuList);
menuList.sort(Comparator.comparingInt(SysMenu::getSort));
// 总库去掉部分菜单
if ("system".equals(userDetail.getUsername())) {
List<String> removeMenuList = Arrays.asList(removeMenus.split(","));
int size = menuList.size();
for (int i = 0; i < size; i++) {
SysMenu sysMenu = menuList.get(i);
String tempName = sysMenu.getName();
if (removeMenuList.contains(tempName)) {
menuList.remove(sysMenu);
size -= 1;
i -= 1;
}
}
}
RoleTree loginMenus = getLoginMenus(menuList);
HashSet<String> set = new HashSet<>();
for (SysMenu sysMenu : menuList) {
set.add(sysMenu.getPerms());
}
if (set.contains("interactCenter")) {
//看是否勾选了首页配置 121是首页菜单的id
loginMenus.getInteractCenter().add("modelList");
loginMenus.getInteractCenter().add("modelPage");
}
if (set.contains("robotCenter")) {
//看是否勾选了首页配置 121是首页菜单的id
loginMenus.getRobotCenter().add("robotPage");
}
if (set.contains("processCenter")) {
//看是否勾选了首页配置 121是首页菜单的id
loginMenus.getProcessCenter().add("processPage");
}
userDetail.setMenus(loginMenus);
// SysPlatformSetting platformInfo = relPlatformSettingService.getPlatformInfoByCurrentTenantId(userDetail.getTenantId());
// SysPlatformSetting newSysPlatformSetting = new SysPlatformSetting();
// BeanUtil.copyProperties(platformInfo, newSysPlatformSetting);
// newSysPlatformSetting.setDocKnowledge(0);
// userDetail.setSysPlatformSetting(newSysPlatformSetting);
redisUtils.set(token, userDetail, Long.parseLong(expireTime));
return token;
}
@Override
public List<String> getRoleIdByUserId(String tenantId, String userId, String path) {
List<ModulePermissions> permissions = commonMapper.getRoleByTenantId(tenantId);
Map<String, List<ModulePermissions>> userMap = permissions.stream().filter(per -> per.getType() == 1).collect(Collectors.groupingBy(ModulePermissions::getRelId));
Map<String, List<ModulePermissions>> deptMap = permissions.stream().filter(per -> per.getType() == 0).collect(Collectors.groupingBy(ModulePermissions::getRelId));
List<String> roleList = new ArrayList<>();
if (userMap.containsKey(userId)) {
List<ModulePermissions> tempPers = userMap.get(userId);
for (ModulePermissions tempPer : tempPers) {
roleList.add(tempPer.getTargetId());
}
}
if (path == null && tenantId.equals("1")) {
List<String> admin = new ArrayList<>();
admin.add("1");
return admin;
}
if (StrUtil.isNotBlank(path)) {
String[] split = path.split(CategoryConstant.ROOT_PATH);
List<String> deptList = Arrays.asList(split).stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
for (String dept : deptList) {
if (deptMap.containsKey(dept)) {
List<ModulePermissions> tempPers = deptMap.get(dept);
for (ModulePermissions tempPer : tempPers) {
roleList.add(tempPer.getTargetId());
}
}
}
}
return roleList;
}
@Override
public RoleTree getLoginMenus(List<SysMenu> menuList) {
RoleTree roleTree = new RoleTree();
if (CollUtil.isEmpty(menuList)) {
return roleTree;
}
menuList.forEach(it -> {
if (it.getParentId() != 0 && StrUtil.isNotBlank(it.getPath())) {
if (it.getParentSystem().contains("tenantCenter")) {
roleTree.getTenantCenter().add(it.getPerms());
} else if (it.getParentSystem().contains("interactCenter")) {
roleTree.getInteractCenter().add(it.getPerms());
} else if (it.getParentSystem().contains("knowledgeCenter")) {
roleTree.getKnowledgeCenter().add(it.getPerms());
} else if (it.getParentSystem().contains("llmCenter")) {
roleTree.getLlmCenter().add(it.getPerms());
} else if (it.getParentSystem().contains("statisticsCenter")) {
roleTree.getStatisticsCenter().add(it.getPerms());
} else if (it.getParentSystem().contains("securityCenter")) {
roleTree.getSecurityCenter().add(it.getPerms());
} else if (it.getParentSystem().contains("robotCenter")) {
roleTree.getRobotCenter().add(it.getPerms());
} else if (it.getParentSystem().contains("staffCenter")) {
roleTree.getStaffCenter().add(it.getPerms());
}else if (it.getParentSystem().contains("processCenter")) {
roleTree.getProcessCenter().add(it.getPerms());
}
}
});
return roleTree;
}
@Override
public Object list(String search, Integer type, Integer userType, String categoryId, String targetId, Integer authType) {
String url = userAdminConfig.getUserAdminDomain() + "/user/list"
+ "?search=" + UrlEncoderUtil.encode(search.trim())
+ "&type=" + type
+ "&userType=2"
+ "&categoryId=" + categoryId
+ "&targetId=" + targetId;
System.out.println(url);
JSONObject object = HttpClientUtils.httpGet(url, UserAdminTokenUtil.getUserAdminToken());
return object;
}
private R loginByAD(ApplicationUserVO.Login login) {
// AD验证
// boolean auth = iLdapService.authenticate(login.getUsername().trim(), login.getPassword().trim());
if (true) {
SysUserSync sysUserSync = sysUserSysMapper.selectOne(new LambdaQueryWrapper<SysUserSync>()
.eq(SysUserSync::getTenantId, permissionTenantId)
.eq(SysUserSync::getUserId, login.getUsername().trim()));
// if (code != 0) {
// throw new RuntimeException("用户系统异常!");
// }
// if (data != null && data.size() > 0) {
// JSONArray array = filterData(data, application);
// if (array != null && array.size() > 0) {
// return R.ok(array);
// } else {
// array = filterData(data, null);
// return R.ok(array);
// }
// } else {
// throw new RuntimeException("用户未授权!");
// }
return R.ok(sysUserSync);
} else {
return R.error("账号/密码错误!");
}
}
}
\ No newline at end of file
package com.cmeeting.ad.util;
import cn.hutool.core.util.StrUtil;
import com.cmeeting.ad.entity.RobotSecurityUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* @Classname JwtUtil
* @Description JWT工具类
* @Author robot
* @Date 2019-05-07 09:23
* @Version 1.0
*/
@Component
public class JwtUtil {
/**
* 用户名称
*/
private static final String USERNAME = Claims.SUBJECT;
/**
* 密钥
*/
private static final String SECRET = "badou1001";
@Value("${jwt.header}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String authTokenStart;
/**
* 生成令牌
*
* @return 令牌
*/
public String generateToken(RobotSecurityUser robotSecurityUser) {
Map<String, Object> claims = new HashMap<>(1);
claims.put(USERNAME, robotSecurityUser.getUsername());
claims.put("time", System.currentTimeMillis());
return generateToken(claims);
}
public String generateToken(String username, String applicationId) {
Map<String, Object> claims = new HashMap<>(1);
claims.put(USERNAME, username);
claims.put("applicationId", applicationId);
claims.put("time", System.currentTimeMillis());
return generateToken(claims);
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String generateToken(Map<String, Object> claims) {
return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, SECRET).compact();
}
/**
* 获取请求token
*
* @param request
* @return
*/
public String getToken(HttpServletRequest request) {
String token = request.getHeader(tokenHeader);
if (StrUtil.isNotBlank(token) && token.length() > authTokenStart.length()) {
token = token.substring(authTokenStart.length()).trim();
}
return token;
}
}
package com.cmeeting.ad.util;
import com.cmeeting.ad.entity.RobotSecurityUser;
import lombok.experimental.UtilityClass;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* @Classname SecurityUtil
* @Description 安全服务工具类
* @Author robot
* @Date 2019-05-08 10:12
* @Version 1.0
*/
@UtilityClass
public class SecurityUtil {
// public void writeJavaScript(R r, HttpServletResponse response) throws IOException {
// response.setStatus(200);
// response.setCharacterEncoding("UTF-8");
// response.setContentType("application/json; charset=utf-8");
// PrintWriter printWriter = response.getWriter();
// printWriter.write(JSON.toJSONString(r));
// printWriter.flush();
// }
/**
* 获取Authentication
*/
private Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
/**
* 获取用户
*
* @return
*/
public RobotSecurityUser getUser() {
try {
return (RobotSecurityUser) getAuthentication().getPrincipal();
} catch (Exception e) {
throw new RuntimeException("登录状态过期");
}
}
/**
* 获取租户信息
*
* @return
*/
public String getTenantId() {
return getUser().getTenantId();
}
public void setUser(RobotSecurityUser robotSecurityUser) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(robotSecurityUser, null, robotSecurityUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
package com.cmeeting.ad.vo;
import lombok.Data;
import java.util.List;
@Data
public class ApplicationUserVO {
@Data
public static class AddUser {
private String id;
String name;
String username;
String password;
String applicationId;
}
@Data
public static class LoginDecrypt {
String data;
String applicationId;
String agentId;
}
@Data
public static class Sso {
String param;
String applicationId;
}
@Data
public static class Auth {
String name;
String userId;
String applicationId;
}
@Data
public static class LoginOutVO {
String userId;
String applicationId;
}
@Data
public static class StatusVO {
List<String> idList;
Integer status;
}
@Data
public static class DisabledVO {
List<String> idList;
/**
* 0启用 1禁用
*/
Integer disabled;
}
@Data
public static class Id {
String id;
}
@Data
public static class Login {
String username;
String password;
String type; //登录方式: password 密码登录 or code 验证码登录
// String key;
// String code;
String phoneCode;
}
@Data
public static class ListVO {
private String fromDate;
/**
* 结束日期
*/
private String toDate;
private Integer current;
private Integer size;
private String content;
private Integer status;
private String applicationId;
}
@Data
public static class Add {
private String name;
private String phone;
private String password;
private String categoryId;
private String applicationId;
}
@Data
public static class Edit {
private String id;
private String name;
private String phone;
private String password;
private Integer status;
}
@Data
public static class BatchEdit{
private List<String> ids;
private Integer status;
}
@Data
public static class SyncSave {
private String applicationId;
private List<SyncData> syncData;
@Data
public static class SyncData {
private String id; //用户id或者部门id
private Integer type; //0 分类 1用户
}
}
@Data
public static class BatchDelete{
private List<String> idList;
}
}
package com.cmeeting.ad.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author liuzhenmeng
* @date 2024/10/31 15:18
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApplicationVo {
private String applicationId;
private String applicationName;
private Integer applicationType;
private String tenantId;
}
package com.cmeeting.ad.vo;
import lombok.Data;
/**
* 登录
*/
@Data
public class UserVo {
/**
* 登录
*/
@Data
public static class Login {
String username;
String password;
String key;
}
@Data
public static class officialSiteLogin {
String username;
String password;
}
@Data
public static class LoginDecrypt {
String data;
}
@Data
public static class Auth {
/**
* 用户id (系统和应用用户的主键id,同步用户的userId)
*/
private String id;
private String tenantId;
/**
* 用户类型:0. 系统用户 1. 应用用户 2. 同步用户
*/
private Integer type;
private String username;
private String nick;
private String path;
}
/**
* 更新昵称和头像
*/
@Data
public static class UpdateNick {
private String nick;
private String picture;
}
/**
* 更新密码
*/
@Data
public static class UpdatePassword {
private String password;
private String newPassword;
private String repeatPassword;
}
}
package com.cmeeting.config; package com.cmeeting.config;
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.SqlSessionTemplate;
...@@ -29,11 +27,16 @@ public class MasterDataSourceConfig { ...@@ -29,11 +27,16 @@ public class MasterDataSourceConfig {
return DataSourceBuilder.create().build(); return DataSourceBuilder.create().build();
} }
/**
* 分页插件(3.3.0 版本使用旧版 PaginationInterceptor)
*/
@Bean @Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() { public PaginationInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 设置数据库类型
return interceptor; // 溢出总页数后是否进行处理 (默认不处理,会返回最后一页数据)
paginationInterceptor.setOverflow(true);
return paginationInterceptor;
} }
@Primary @Primary
...@@ -42,7 +45,7 @@ public class MasterDataSourceConfig { ...@@ -42,7 +45,7 @@ public class MasterDataSourceConfig {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean(); MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource); bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/primary/*.xml")); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/primary/*.xml"));
bean.setPlugins(mybatisPlusInterceptor()); bean.setPlugins(paginationInterceptor());
return bean.getObject(); return bean.getObject();
} }
......
package com.cmeeting.constant;
/**
* @author 王长伟
*/
public class CategoryConstant {
public static final String ALL = "全部";
public static final String ALL_EN = "all";
/**
* 根父级id
*/
public static final String ROOT_ID = "0";
/**
* 根级路径
*/
public static final String ROOT_PATH = "/";
}
package com.cmeeting.constant;
public interface FilterConstant {
String ERROR_CONTROLLER_PATH = "/error/throw";
}
package com.cmeeting.constant;
public interface LoginTypeConstant {
String PASSWORD = "password";
String CODE = "code";
String AD = "ad";
}
package com.cmeeting.constant;
/**
* 登录的用户类型
*/
public interface LoginUserConstant {
Integer SYS = 0;
Integer APPLICATION = 1;
Integer SYNC = 2;
}
package com.cmeeting.constant;
/**
* 用户系统功能接口
*
* @author hong
*/
public interface UserAdminRouteConstant {
String ACCESS_TOKEN = "/chatbot/getAccessToken";
/**
* 自建用户
*/
interface SysUser {
/**
* 分类查询接口
*/
String CATEGORY_TREE = "/chatbot/user/category/tree";
/**
* 创建系统自建用户分类
*/
String ADD_CATEGORY = "/chatbot/user/category/add";
/**
* 编辑用户分类
*/
String EDIT_CATEGORY = "/chatbot/user/category/edit";
/**
* 删除用户分类
*/
String DELETE_CATEGORY = "/chatbot/user/category/remove";
/**
* 查询用户列表
*/
String UPDATE_PASS = "/chatbot/user/updatePass";
String LIST = "/chatbot/user/list";
/**
* 创建用户
*/
String ADD = "/chatbot/user/add";
String COUNT = "/chatbot/user/count";
/**
* 更新用户
*/
String EDIT = "/chatbot/user/edit";
/**
* 删除用户
*/
String DELETE = "/chatbot/user/delete";
/**
* 批量删除用户
*/
String BATCH_DELETE = "/chatbot/user/deleteBatchIds";
/**
* 用户详情
*/
String INFO = "/chatbot/user/info";
/**
* 根据条件查询所有三类用户
*/
String FIND = "/chatbot/user/find";
String FIND_TENANT_ID = "/chatbot/user/findTenantIdByUserId";
String FIND_BY_UID = "/chatbot/user/findByUid";
String FIND_USER_BY_UID = "/chatbot/user/findUserByUid";
String FIND_NAME = "/chatbot/user/findName"; //根据用户和分类ID查询对应name
String FIND_ADMIN_NAME = "/chatbot/user/findAdminName"; //超管查询根据用户和分类ID查询对应name、checked
String FIND_SHARE_NAME = "/chatbot/user/findShareName"; //超管查询根据用户和分类ID查询对应name、checked
String INIT = "/chatbot/user/init"; // 创建租户的时候 初始化用户系统 Get请求 参数:tenantId, applicationId
String PUSH = "/chatbot/user/push";
String EXPORT = "/chatbot/user/export";
}
/**
* 同步用户
*/
interface SyncUser {
String CATEGORY_TREE = "/chatbot/user/sync/category/tree"; // 查询分类
String LIST = "/chatbot/user/sync/list"; //查询用户
String FIELD_INFO = "/chatbot/user/sync/field/info"; //查询属性
String FIELD_UPDATE = "/chatbot/user/sync/field/update"; //更新属性值
// String ADD = "/chatbot/user/sync/add"; //新增
String TREE = "/chatbot/user/sync/tree"; //用户查询
String INFO = "/chatbot/user/sync/info"; //用户详情
String PUSH = "/chatbot/user/sync/push"; //用户同步
String ZHONGJI_PUSH = "/chatbot/user/sync/zhongji_push"; //用户同步-中集同步接口
String INIT_TEMP_TABLE = "/chatbot/user/sync/initTempTable"; //初始化临时表
String HANDEL_PTAH = "/chatbot/user/sync/category/handelPath"; //用户同步后处理分类
String COMMIT_OR_ROLLBACK = "/chatbot/user/sync/commitOrRollback"; //用户同步后处理分类
String REMOVE = "/chatbot/user/sync/field/remove"; //属性删除
String MOVE = "/chatbot/user/sync/field/move"; //用户同步
String INIT = "/chatbot/user/sync/init"; //用户同步
String DEPART = "/chatbot/user/sync/getAllDeptByUserId"; //用户部门
String TRUNCATE = "/chatbot/user/sync/truncate"; //用户部门
String ALL_CHILDREN = "/chatbot/user/sync/children"; //查询部门下所有子部门id以及用户id
String BATCH_DELETE = "/chatbot/user/sync/batchDelete"; //用户部门
String FIND_DEPT_NAME = "/chatbot/user/sync/getAllDeptByUserId";//查询部门全路径//用户同步
String ADMIN_LIST = "/chatbot/user/sync/adminList"; //超管同步用户授权给租户查询
String AUTH_LIST = "/chatbot/user/sync/authList"; //超管同步用户已授权用户/部门查询
String AUTH = "/chatbot/user/sync/auth"; //超管同步用户已授权用户/部门查询
String REMOVE_AUTH = "/chatbot/user/sync/removeAuth"; //超管同步用户已授权用户/部门查询
String SHARE_TREE = "/chatbot/shareUser/tree"; //超管分享的部门查询
String SHARE_LIST = "/chatbot/shareUser/list"; //超管分享的用户查询
}
/**
* 应用用户
*/
interface ApplicationUser {
String ADD_CATEGORY = "/chatbot/user/application/category/add"; // 新增分类
String EDIT_CATEGORY = "/chatbot/user/application/category/edit"; // 编辑分类
String CATEGORY_TREE = "/chatbot/user/application/category/tree"; // 查询分类
String DELETE_CATEGORY = "/chatbot/user/application/category/remove"; // 删除分类
String ADD = "/chatbot/user/application/add"; // 新增用户
String EDIT = "/chatbot/user/application/edit"; // 编辑用户信息
String BATCH_EDIT = "/chatbot/user/application/batchEdit"; // 编辑用户信息
String BATCH_DELETE = "/chatbot/user/application/batchDelete"; // 编辑用户信息
String DELETE = "/chatbot/user/application/delete"; // 删除用户
String LIST = "/chatbot/user/application/list"; // 用户列表
String INFO = "/chatbot/user/application/info"; // 用户详情
}
interface Report{
String COUNT = "/chatbot/report/user/count"; // 应用用户+系统用户 总计和新增
}
}
...@@ -4,12 +4,14 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; ...@@ -4,12 +4,14 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cmeeting.pojo.MeetingRecordTemplate; import com.cmeeting.pojo.MeetingRecordTemplate;
import com.cmeeting.service.MeetingRecordTemplateService; import com.cmeeting.service.MeetingRecordTemplateService;
import com.cmeeting.service.RecordTemplatePermissionService;
import com.cmeeting.util.R; import com.cmeeting.util.R;
import com.cmeeting.vo.RecordTemplateVO; import com.cmeeting.vo.RecordTemplateVO;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDateTime;
...@@ -18,6 +20,8 @@ import java.time.LocalDateTime; ...@@ -18,6 +20,8 @@ import java.time.LocalDateTime;
public class RecordTemplateController { public class RecordTemplateController {
@Autowired @Autowired
private MeetingRecordTemplateService recordTemplateService; private MeetingRecordTemplateService recordTemplateService;
@Resource
private RecordTemplatePermissionService recordTemplatePermissionService;
@PostMapping("/create") @PostMapping("/create")
public R create(@RequestBody MeetingRecordTemplate recordTemplate) { public R create(@RequestBody MeetingRecordTemplate recordTemplate) {
...@@ -77,4 +81,9 @@ public class RecordTemplateController { ...@@ -77,4 +81,9 @@ public class RecordTemplateController {
String template = recordTemplateService.templateUpload(file, id); String template = recordTemplateService.templateUpload(file, id);
return R.ok(template); return R.ok(template);
} }
@PostMapping("/auth")
public R auth(@RequestBody RecordTemplateVO.TemplatePermissionVO vo) {
return R.ok(recordTemplatePermissionService.auth(vo));
}
} }
// controller/UserController.java
package com.cmeeting.controller;
import com.cmeeting.dto.LoginDTO;
import com.cmeeting.service.UserService;
import com.cmeeting.vo.LoginVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
public LoginVO login(@RequestBody LoginDTO loginDTO) {
return userService.login(loginDTO);
}
}
\ No newline at end of file
...@@ -31,4 +31,14 @@ public class UserDTO { ...@@ -31,4 +31,14 @@ public class UserDTO {
* 邮箱 * 邮箱
*/ */
private String email; private String email;
@Override
public String toString() {
return "UserDTO{" +
"userName='" + userName + '\'' +
", wid='" + wid + '\'' +
", tid='" + tid + '\'' +
", email='" + email + '\'' +
'}';
}
} }
\ No newline at end of file
...@@ -93,7 +93,6 @@ public class CmeetingJob { ...@@ -93,7 +93,6 @@ public class CmeetingJob {
} }
// @Scheduled(fixedRate = 10 * 60 * 1000,initialDelay = 2 * 60 * 1000) // @Scheduled(fixedRate = 10 * 60 * 1000,initialDelay = 2 * 60 * 1000)
@Scheduled(fixedRate = 10 * 60 * 1000)
public void execute() { public void execute() {
// 定义时间格式化器 // 定义时间格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
...@@ -115,6 +114,8 @@ public class CmeetingJob { ...@@ -115,6 +114,8 @@ public class CmeetingJob {
if (CollectionUtils.isEmpty(accessUserIds)) { if (CollectionUtils.isEmpty(accessUserIds)) {
log.info("无生成纪要权限的人员"); log.info("无生成纪要权限的人员");
return; return;
}else{
log.info("生成纪要权限人员:->{}",accessUserIds.toString());
} }
List<TencentMeetingVO.RecordFile> meetingFiles = tencentMeetingService.getMeetingFiles(accessUserIds); List<TencentMeetingVO.RecordFile> meetingFiles = tencentMeetingService.getMeetingFiles(accessUserIds);
...@@ -131,7 +132,8 @@ public class CmeetingJob { ...@@ -131,7 +132,8 @@ public class CmeetingJob {
/** /**
* 定时扫描早于一小时之前的,所有未重试过的会议,重新生成纪要 * 定时扫描早于一小时之前的,所有未重试过的会议,重新生成纪要
*/ */
@Scheduled(fixedRate = 30 * 60 * 1000,initialDelay = 10 * 60 * 1000) // @Scheduled(fixedRate = 30 * 60 * 1000,initialDelay = 10 * 60 * 1000)
@Scheduled(fixedRate = 30 * 60 * 1000)
public void meetingMinutesRetry() { public void meetingMinutesRetry() {
try { try {
log.info("-------生成纪要重试定时任务开始-------"); log.info("-------生成纪要重试定时任务开始-------");
......
...@@ -58,11 +58,9 @@ public class EmailPushTask { ...@@ -58,11 +58,9 @@ public class EmailPushTask {
.eq(subMeetingId != null, MeetingInfo::getSubMeetingId, subMeetingId)); .eq(subMeetingId != null, MeetingInfo::getSubMeetingId, subMeetingId));
while (retryCount.intValue() <= MAX_RETRY && !isSuccess) { while (retryCount.intValue() <= MAX_RETRY && !isSuccess) {
try { try {
String targetFileName;
String meetingName; String meetingName;
String recordXmlPath = meetingId + "-recordXmlPath-" + IdUtil.fastSimpleUUID() + "xml";
String xml; String xml;
try(InputStream is = minioUtils.getFile(recordXmlPath)){ try(InputStream is = minioUtils.getFile(meetingInfo.getRecordXml())){
xml = convertInputStreamToString(is); xml = convertInputStreamToString(is);
}catch (Exception e){ }catch (Exception e){
log.error(e.getMessage()); log.error(e.getMessage());
...@@ -86,7 +84,7 @@ public class EmailPushTask { ...@@ -86,7 +84,7 @@ public class EmailPushTask {
XWPFTemplate template = XWPFTemplate.compile(inputStream).render(dataModel); XWPFTemplate template = XWPFTemplate.compile(inputStream).render(dataModel);
template.writeAndClose(new FileOutputStream(dataNetworkMinutesPath)); template.writeAndClose(new FileOutputStream(dataNetworkMinutesPath));
byte[] recordXmlData = Files.readAllBytes(Paths.get(dataNetworkMinutesPath)); byte[] recordXmlData = Files.readAllBytes(Paths.get(dataNetworkMinutesPath));
minioUtils.upload(recordXmlPath,recordXmlData); minioUtils.upload(meetingInfo.getRecordXml(),recordXmlData);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
......
...@@ -25,8 +25,7 @@ import com.tencentcloudapi.wemeet.Client; ...@@ -25,8 +25,7 @@ import com.tencentcloudapi.wemeet.Client;
import com.tencentcloudapi.wemeet.core.authenticator.AuthenticatorBuilder; import com.tencentcloudapi.wemeet.core.authenticator.AuthenticatorBuilder;
import com.tencentcloudapi.wemeet.core.authenticator.JWTAuthenticator; import com.tencentcloudapi.wemeet.core.authenticator.JWTAuthenticator;
import com.tencentcloudapi.wemeet.service.meetings.api.MeetingsApi; import com.tencentcloudapi.wemeet.service.meetings.api.MeetingsApi;
import com.tencentcloudapi.wemeet.service.meetings.model.V1MeetingsMeetingIdParticipantsGet200Response; import com.tencentcloudapi.wemeet.service.meetings.model.*;
import com.tencentcloudapi.wemeet.service.meetings.model.V1MeetingsMeetingIdParticipantsGet200ResponseParticipantsInner;
import com.tencentcloudapi.wemeet.service.records.api.RecordsApi; import com.tencentcloudapi.wemeet.service.records.api.RecordsApi;
import com.tencentcloudapi.wemeet.service.records.model.V1AddressesRecordFileIdGet200Response; import com.tencentcloudapi.wemeet.service.records.model.V1AddressesRecordFileIdGet200Response;
import com.tencentcloudapi.wemeet.service.records.model.V1AddressesRecordFileIdGet200ResponseAiMeetingTranscriptsInner; import com.tencentcloudapi.wemeet.service.records.model.V1AddressesRecordFileIdGet200ResponseAiMeetingTranscriptsInner;
...@@ -42,6 +41,7 @@ import org.apache.poi.xwpf.extractor.XWPFWordExtractor; ...@@ -42,6 +41,7 @@ import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.io.*; import java.io.*;
import java.math.BigInteger; import java.math.BigInteger;
...@@ -51,7 +51,10 @@ import java.nio.file.Files; ...@@ -51,7 +51,10 @@ import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
...@@ -94,9 +97,50 @@ public class FileProcessTask { ...@@ -94,9 +97,50 @@ public class FileProcessTask {
.withSecret(tencentSecretId,tencentSecretKey) .withSecret(tencentSecretId,tencentSecretKey)
.build(); .build();
try { try {
//已保存的会议信息
MeetingInfo meetingInfo = meetingInfoMapper.selectOne(new LambdaQueryWrapper<MeetingInfo>() MeetingInfo meetingInfo = meetingInfoMapper.selectOne(new LambdaQueryWrapper<MeetingInfo>()
.eq(MeetingInfo::getMeetingId,meetingId) .eq(MeetingInfo::getMeetingId,meetingId)
.eq(subMeetingId != null, MeetingInfo::getSubMeetingId, subMeetingId)); .eq(subMeetingId != null, MeetingInfo::getSubMeetingId, subMeetingId));
//下面再查一遍会议信息的意义是,为了获取会议中的子会议Id,如果是周期会议,需要保存下来(重要)
MeetingsApi.ApiV1MeetingsMeetingIdGetRequest meetingRequest =
new MeetingsApi.ApiV1MeetingsMeetingIdGetRequest.Builder(meetingId)
.operatorId(tencentAdminUserId)
.operatorIdType("1")
.instanceid("0")
.build();
MeetingsApi.ApiV1MeetingsMeetingIdGetResponse meetingResponse =
client.meetings().v1MeetingsMeetingIdGet(meetingRequest, new JWTAuthenticator.Builder()
.nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt())))
.timestamp(String.valueOf(System.currentTimeMillis() / 1000L)));
V1MeetingsMeetingIdGet200Response meetingResponseData = meetingResponse.getData();
List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner> meetingInfoList = meetingResponseData.getMeetingInfoList();
V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner meetingItem = meetingInfoList.get(0);
//会议类型
//0:一次性会议
//1:周期性会议
Long meetingType = meetingItem.getMeetingType();
if(meetingType.intValue() == 1){
//如果是周期会议,获取子会议的ID,用于查询参会人员
List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner> subMeetings = meetingItem.getSubMeetings();
//如果主持人突然取消了后续的所有周期会议,subMeetings列表会返回null
if(!CollectionUtils.isEmpty(subMeetings)){
LocalDate meetingStartDate = meetingInfo.getStartTime().toLocalDate();
Optional<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner> subMeeting = subMeetings.stream().filter(item ->
Instant.ofEpochSecond(Long.valueOf(item.getStartTime())).atZone(ZoneId.systemDefault()).toLocalDate().equals(meetingStartDate))
.findFirst();
if(!subMeeting.isPresent()){
log.error("周期会议"+meetingId+"未知子会议ID");
// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"未知子会议ID");
retryCount++;
continue;
}
subMeetingId = subMeeting.get().getSubMeetingId();
}else{
subMeetingId = meetingItem.getCurrentSubMeetingId();
log.info("周期会议"+meetingId+"的子会议列表为空,获取当前子会议号");
// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"的子会议列表为空,获取当前子会议号");
}
}
// 获取参会成员明细 // 获取参会成员明细
MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest participantsRequest = MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest participantsRequest =
...@@ -502,8 +546,10 @@ public class FileProcessTask { ...@@ -502,8 +546,10 @@ public class FileProcessTask {
//特殊处理map格式 //特殊处理map格式
for (Map.Entry<String, Object> entry : map.entrySet()) { for (Map.Entry<String, Object> entry : map.entrySet()) {
Map<String,Object> value = (Map<String, Object>) entry.getValue(); Map<String,Object> value = (Map<String, Object>) entry.getValue();
Object realValue = value.get(""); //取出正确的value并设置
entry.setValue(realValue); String realValue = String.valueOf(value.get("")).replaceAll("^\\n+","");
//内容段首移除换行,段末追加换行(会议名称结尾不换行
entry.setValue(realValue.endsWith("\n") || "meeting_name".equals(entry.getKey()) || "meeting_purpose".equals(entry.getKey()) ? realValue : realValue + "\n");
} }
return map; return map;
} }
...@@ -514,7 +560,7 @@ public class FileProcessTask { ...@@ -514,7 +560,7 @@ public class FileProcessTask {
* @return * @return
*/ */
private String extractXmlFromMarkdown(String markdown) { private String extractXmlFromMarkdown(String markdown) {
StringBuffer sb = null; StringBuffer sb;
try { try {
int start = markdown.indexOf("<"); int start = markdown.indexOf("<");
if(start == -1){ if(start == -1){
...@@ -523,7 +569,8 @@ public class FileProcessTask { ...@@ -523,7 +569,8 @@ public class FileProcessTask {
int end = markdown.lastIndexOf(">") + 1; int end = markdown.lastIndexOf(">") + 1;
sb = new StringBuffer(); sb = new StringBuffer();
sb.append("<root>"); sb.append("<root>");
sb.append(markdown.substring(start, end).trim()); String xml = markdown.substring(start, end).trim().replaceAll("\n\n","\n");
sb.append(xml);
sb.append("</root>"); sb.append("</root>");
} catch (Exception e) { } catch (Exception e) {
log.info("markdown转xml,markdown->{}",markdown); log.info("markdown转xml,markdown->{}",markdown);
......
package com.cmeeting.mapper.primary;
import com.baomidou.mybatisplus.annotation.SqlParser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmeeting.ad.entity.ModulePermissions;
import com.cmeeting.ad.entity.SysMenu;
import com.cmeeting.ad.vo.ApplicationVo;
import com.cmeeting.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* 租户表 Mapper 接口
* </p>
*
* @author robot
* @since 2019-08-10
*/
public interface CommonMapper extends BaseMapper<User> {
/**
* 根据租户id获取菜单列表
* @param tenantId
* @return
*/
@SqlParser(filter = true)
List<SysMenu> getMenuList(@Param("tenantId")String tenantId);
@SqlParser(filter = true)
List<ModulePermissions> getRoleByTenantId(@Param("tenantId") String tenantId);
@SqlParser(filter = true)
List<SysMenu> getMenuByRoleList(@Param("roleIdList") List<String> roleIdList, @Param("tenantId") String tenantId);
@SqlParser(filter = true)
ApplicationVo getApplicationNoTenantId(@Param("applicationId")String applicationId);
List<String> getSubDeptId(@Param(value = "deptId")String deptId);
List<String> getUsersByDept(@Param(value = "deptIds") List<String> deptIds);
}
package com.cmeeting.mapper.primary;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmeeting.pojo.RecordTemplatePermission;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface RecordTemplatePermissionMapper extends BaseMapper<RecordTemplatePermission> {
void batchInsert(@Param("permissions")List<RecordTemplatePermission> permissions);
}
\ No newline at end of file
package com.cmeeting.mapper.primary;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmeeting.ad.entity.SysTenant;
/**
* <p>
* 租户表 Mapper 接口
* </p>
*
* @author robot
* @since 2019-08-10
*/
public interface SysTenantMapper extends BaseMapper<SysTenant> {
}
package com.cmeeting.mapper.secondary; package com.cmeeting.mapper.secondary;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmeeting.ad.entity.SysUserSync;
import com.cmeeting.dto.UserDTO; import com.cmeeting.dto.UserDTO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
...@@ -7,7 +9,7 @@ import org.apache.ibatis.annotations.Param; ...@@ -7,7 +9,7 @@ import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
@Mapper @Mapper
public interface SysUserSysMapper { public interface SysUserSysMapper extends BaseMapper<SysUserSync> {
String getCompanyEmail(String wid, String tenantId); String getCompanyEmail(String wid, String tenantId);
String getParentDeptId(String deptId, String tenantId); String getParentDeptId(String deptId, String tenantId);
......
package com.cmeeting.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 会议纪要模板实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("cmt_record_template_permission")
public class RecordTemplatePermission implements Serializable {
private static final long serialVersionUID = -26238487532381000L;
/**
* 主键id
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 模板id
*/
@TableId(type = IdType.AUTO)
private Integer templateId;
/**
* 模板类型
*/
private String templateType;
/**
* 对象类型
*/
private String relType;
/**
* 对象id
*/
private String relId;
/**
* 创建时间
*/
private LocalDateTime createTime;
}
\ No newline at end of file
package com.cmeeting.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cmeeting.pojo.RecordTemplatePermission;
import com.cmeeting.vo.RecordTemplateVO;
public interface RecordTemplatePermissionService extends IService<RecordTemplatePermission> {
boolean auth(RecordTemplateVO.TemplatePermissionVO vo);
}
// service/UserService.java
package com.cmeeting.service;
import com.cmeeting.dto.LoginDTO;
import com.cmeeting.vo.LoginVO;
public interface UserService {
LoginVO login(LoginDTO loginDTO);
}
\ No newline at end of file
package com.cmeeting.service.impl; package com.cmeeting.service.impl;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
...@@ -55,35 +58,22 @@ public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, Meeti ...@@ -55,35 +58,22 @@ public class MeetingInfoServiceImpl extends ServiceImpl<MeetingInfoMapper, Meeti
} }
private String convertJSONToXml(String json) { private String convertJSONToXml(String json) {
try { JSONArray jsonArray = JSON.parseArray(json);;
// 解析 JSON 字符串 StringBuilder xmlBuilder = new StringBuilder();
ObjectMapper jsonMapper = new ObjectMapper();
List<Map<String, String>> list = jsonMapper.readValue(json, List.class);
// 创建 XML 根节点 xmlBuilder.append("<root>");
Map<String, Object> root = new HashMap<>(); for (int i = 0; i < jsonArray.size(); i++) {
JSONObject jsonObj = jsonArray.getJSONObject(i);
// 处理每个 JSON 项 String key = jsonObj.getString("key");
for (Map<String, String> item : list) { String label = jsonObj.getString("keyName");
String keyValue = item.get("key"); String value = jsonObj.getString("value");
String keyName = item.get("keyName");
String value = item.get("value");
// 创建子节点 xmlBuilder.append(String.format("<%s label=\"%s\">%s</%s>",
Map<String, Object> subNode = new HashMap<>(); key, label, value, key));
subNode.put("label", keyName);
subNode.put("", value);
// 添加到根节点
root.put(keyValue, subNode);
}
// 转换为 XML
XmlMapper xmlMapper = new XmlMapper();
return xmlMapper.writeValueAsString(root);
} catch (IOException e) {
log.error("JSON 转 XML 失败: " + e.getMessage());
throw new RuntimeException("JSON 转 XML 失败", e);
} }
xmlBuilder.append("</root>");
return xmlBuilder.toString();
} }
} }
package com.cmeeting.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmeeting.mapper.primary.CommonMapper;
import com.cmeeting.mapper.primary.RecordTemplatePermissionMapper;
import com.cmeeting.pojo.CoreModulePermissions;
import com.cmeeting.pojo.MeetingRecordTemplate;
import com.cmeeting.pojo.RecordTemplatePermission;
import com.cmeeting.service.MeetingRecordTemplateService;
import com.cmeeting.service.RecordTemplatePermissionService;
import com.cmeeting.vo.RecordTemplateVO;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class RecordTemplatePermissionServiceImpl extends ServiceImpl<RecordTemplatePermissionMapper, RecordTemplatePermission> implements RecordTemplatePermissionService {
@Resource
private MeetingRecordTemplateService recordTemplateService;
@Resource
private CommonMapper commonMapper;
/**
* 授权
* @param vo
*/
@Override
public boolean auth(RecordTemplateVO.TemplatePermissionVO vo) {
List<RecordTemplateVO.PermissionItem> permissionItems = vo.getPermissionItems();
if(CollectionUtils.isEmpty(permissionItems) || vo.getTemplateId() == null){
return false;
}
MeetingRecordTemplate template = recordTemplateService.getById(vo.getTemplateId());
if(template == null){
throw new RuntimeException("模板不存在");
}
List<RecordTemplatePermission> permissions = new ArrayList<>();
// List<RecordTemplateVO.PermissionItem> deptPermissions = permissionItems.stream().filter(item -> "1".equals(item.getRelType())).collect(Collectors.toList());
// List<RecordTemplateVO.PermissionItem> userPermissions = permissionItems.stream().filter(item -> "2".equals(item.getRelType())).collect(Collectors.toList());
// for (RecordTemplateVO.PermissionItem userPermission : userPermissions) {
// RecordTemplatePermission permission = RecordTemplatePermission.builder()
// .templateId(vo.getTemplateId())
// .templateType(template.getType())
// .relType(userPermission.getRelType())
// .relId(userPermission.getRelId())
// .createTime(now)
// .build();
// permissions.add(permission);
// }
// List<String> deptPath = new ArrayList<>();
// for (RecordTemplateVO.PermissionItem deptPermission : deptPermissions) {
// String deptId = deptPermission.getRelId();
// getDeptPath(deptPath,deptId);
// }
// List<String> accessUserIds = !CollectionUtils.isEmpty(deptPath) ? commonMapper.getUsersByDept(deptPath) : new ArrayList<>();
LocalDateTime now = LocalDateTime.now();
for (RecordTemplateVO.PermissionItem permissionItem : permissionItems) {
RecordTemplatePermission permission = RecordTemplatePermission.builder()
.templateId(vo.getTemplateId())
.templateType(template.getType())
.relType(permissionItem.getRelType())
.relId(permissionItem.getRelId())
.createTime(now)
.build();
permissions.add(permission);
}
baseMapper.batchInsert(permissions);
return true;
}
/**
* 获取部门的路径
* @param deptPath
* @param deptId
*/
private void getDeptPath(List<String> deptPath, String deptId) {
if(!deptPath.contains(deptId)) deptPath.add(deptId);
List<String> subDeptIds = commonMapper.getSubDeptId(deptId);
if(CollectionUtils.isEmpty(subDeptIds)) return;
for (String subDeptId : subDeptIds) {
//部门id去重
if(!deptPath.contains(subDeptId)){
deptPath.add(subDeptId);
getDeptPath(deptPath,subDeptId);
}
}
}
}
\ No newline at end of file
...@@ -337,7 +337,10 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -337,7 +337,10 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
long startTime = now.minusDays(2).toEpochSecond(); long startTime = now.minusDays(2).toEpochSecond();
long endTime = now.toEpochSecond(); long endTime = now.toEpochSecond();
AtomicInteger currentPage = new AtomicInteger(1); AtomicInteger currentPage = new AtomicInteger(1);
Integer totalPage = 1;
//获取总页数
CorpRecordsVO firstData = fetchMeetingRecords(tencentAdminUserId, 1, startTime, endTime, 1, 20);
Integer totalPage = firstData.getTotalPage();
//目前已存储的会议id //目前已存储的会议id
List<String> meetingIds = meetingInfoMapper.getAllMeetingIds(); List<String> meetingIds = meetingInfoMapper.getAllMeetingIds();
...@@ -346,7 +349,6 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -346,7 +349,6 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
while (currentPage.intValue() <= totalPage){ while (currentPage.intValue() <= totalPage){
CorpRecordsVO data = fetchMeetingRecords(tencentAdminUserId, 1, startTime, endTime, currentPage.getAndIncrement(), 20); CorpRecordsVO data = fetchMeetingRecords(tencentAdminUserId, 1, startTime, endTime, currentPage.getAndIncrement(), 20);
//设置总页数 //设置总页数
totalPage = data.getTotalPage();
if (data != null && data.getRecordMeetings() != null && !data.getRecordMeetings().isEmpty()) { if (data != null && data.getRecordMeetings() != null && !data.getRecordMeetings().isEmpty()) {
List<CorpRecordsVO.RecordMeeting> meetings = data.getRecordMeetings(); List<CorpRecordsVO.RecordMeeting> meetings = data.getRecordMeetings();
...@@ -377,64 +379,41 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -377,64 +379,41 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
.instanceid("0") .instanceid("0")
.build(); .build();
try { try {
MeetingsApi.ApiV1MeetingsMeetingIdGetResponse meetingResponse = //如果数据库中已有相同会议id的记录,跳过同步
client.meetings().v1MeetingsMeetingIdGet(meetingRequest, new JWTAuthenticator.Builder() if(!meetingIds.contains(meetingId)){
.nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt()))) log.info("【会议检索】新的会议meetingId->{}",meeting.getMeetingId());
.timestamp(String.valueOf(System.currentTimeMillis() / 1000L))); List<CorpRecordsVO.RecordFile> recordFiles = meeting.getRecordFiles();
V1MeetingsMeetingIdGet200Response meetingResponseData = meetingResponse.getData(); //按转录文件时间升序,便于后续的内容拼接
List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner> meetingInfoList = meetingResponseData.getMeetingInfoList(); List<String> recordFileIdList = recordFiles.stream().sorted(Comparator.comparingLong(CorpRecordsVO.RecordFile::getRecordStartTime))
//尝试获取会议详情 .map(CorpRecordsVO.RecordFile::getRecordFileId).collect(Collectors.toList());
if(meetingInfoList != null && meetingInfoList.size() > 0){ TencentMeetingVO.RecordFile recordFileItem = TencentMeetingVO.RecordFile.builder()
V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner meetingInfo = meetingInfoList.get(0); .recordFileIdList(recordFileIdList).meetingId(meetingId).subMeetingId(subMeetingId).build();
//会议类型
//0:一次性会议 String hostId;
//1:周期性会议 String hostName;
Long meetingType = meetingInfo.getMeetingType(); //优先使用会议列表中已有的主持人字段
if(meetingType.intValue() == 1){ if(StringUtils.isNotEmpty(meeting.getHostUserId())){
//如果是周期会议,获取子会议的ID,用于查询参会人员 hostId = meeting.getHostUserId();
List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner> subMeetings = meetingInfo.getSubMeetings(); hostName = meetingMap.containsKey(hostId) ? meetingMap.get(hostId) :null;
//如果主持人突然取消了后续的所有周期会议,subMeetings列表会返回null log.info("从会议列表中成功获取到主持人信息");
if(!CollectionUtils.isEmpty(subMeetings)){ }else{
LocalDate meetingStartDate = Instant.ofEpochMilli(meeting.getMediaStartTime()).atZone(ZoneId.systemDefault()).toLocalDate(); //判断主持人是否存在,如果主持人未参会,是查不到主持人的
Optional<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInnerSubMeetingsInner> subMeeting = subMeetings.stream().filter(item -> //如果主持人未参会,使用会议详情中的创建人作为主持人
Instant.ofEpochSecond(Long.valueOf(item.getStartTime())).atZone(ZoneId.systemDefault()).toLocalDate().equals(meetingStartDate)) MeetingsApi.ApiV1MeetingsMeetingIdGetResponse meetingResponse =
.findFirst(); client.meetings().v1MeetingsMeetingIdGet(meetingRequest, new JWTAuthenticator.Builder()
if(!subMeeting.isPresent()){ .nonce(BigInteger.valueOf(Math.abs((new SecureRandom()).nextInt())))
log.error("周期会议"+meetingId+"未知子会议ID"); .timestamp(String.valueOf(System.currentTimeMillis() / 1000L)));
// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"未知子会议ID"); V1MeetingsMeetingIdGet200Response meetingResponseData = meetingResponse.getData();
continue; List<V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner> meetingInfoList = meetingResponseData.getMeetingInfoList();
} V1MeetingsMeetingIdGet200ResponseMeetingInfoListInner meetingInfo = meetingInfoList.get(0);
subMeetingId = subMeeting.get().getSubMeetingId(); //会议详情中有主持人信息
}else{ if(!CollectionUtils.isEmpty(meetingInfo.getCurrentHosts())){
subMeetingId = meetingInfo.getCurrentSubMeetingId(); log.info("尝试从会议详情中获取主持人信息");
log.info("周期会议"+meetingId+"的子会议列表为空,获取当前子会议号");
// processLogService.log(meeting.getMeetingId(),subMeetingId,"周期会议"+meetingId+"的子会议列表为空,获取当前子会议号");
}
}
//如果数据库中已有相同会议id的记录,跳过同步
if(!meetingIds.contains(meetingId)){
log.info("【会议检索】新的会议meetingId->{},开始持久化",meeting.getMeetingId());
List<CorpRecordsVO.RecordFile> recordFiles = meeting.getRecordFiles();
//按转录文件时间升序,便于后续的内容拼接
List<String> recordFileIdList = recordFiles.stream().sorted(Comparator.comparingLong(CorpRecordsVO.RecordFile::getRecordStartTime))
.map(CorpRecordsVO.RecordFile::getRecordFileId).collect(Collectors.toList());
TencentMeetingVO.RecordFile recordFileItem = TencentMeetingVO.RecordFile.builder()
.recordFileIdList(recordFileIdList).meetingId(meetingId).subMeetingId(subMeetingId).build();
String hostId;
String hostName;
//优先使用会议列表中已有的主持人字段
if(StringUtils.isNotEmpty(meeting.getHostUserId())){
hostId = meeting.getHostUserId();
hostName = meetingMap.containsKey(hostId) ? meetingMap.get(hostId) :null;
}else if(!CollectionUtils.isEmpty(meetingInfo.getCurrentHosts())){
//判断主持人是否存在,如果主持人未参会,是查不到主持人的
//如果主持人未参会,使用会议详情中的创建人作为主持人
hostId = meetingInfo.getCurrentHosts().get(0).getUserid(); hostId = meetingInfo.getCurrentHosts().get(0).getUserid();
hostName = meetingMap.containsKey(hostId) ? meetingMap.get(hostId) :null; hostName = meetingMap.containsKey(hostId) ? meetingMap.get(hostId) :null;
log.info("主持人会中缺席,默认会议创建人为主持人");
// processLogService.log(meeting.getMeetingId(),subMeetingId,"未找到主持人,默认会议创建人为主持人"); // processLogService.log(meeting.getMeetingId(),subMeetingId,"未找到主持人,默认会议创建人为主持人");
}else{ }else{
log.info("尝试从参会人员列表中获取主持人信息");
// 获取参会成员明细 // 获取参会成员明细
MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest participantsRequest = MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest participantsRequest =
new MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest new MeetingsApi.ApiV1MeetingsMeetingIdParticipantsGetRequest
...@@ -468,36 +447,41 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T ...@@ -468,36 +447,41 @@ public class TencentMeetingServiceImpl extends ServiceImpl<TecentMeetingMapper,T
continue; continue;
} }
} }
String email;
//判断是否有权限生成纪要 }
boolean generateAccess = accessUserIds.stream().anyMatch(item -> item.getTid().equals(hostId)); String email;
if(!generateAccess){ //判断是否有权限生成纪要
log.error("【权限校验】主持人{}没有生成纪要权限,跳过生成",hostId); boolean generateAccess = accessUserIds.stream().anyMatch(item -> item.getTid().equals(hostId));
if(!generateAccess){
log.error("【权限校验】主持人{}没有生成纪要权限,跳过生成",hostId);
// processLogService.log(meeting.getMeetingId(),subMeetingId,"【权限校验】主持人"+hostId+"没有生成纪要权限,跳过生成"); // processLogService.log(meeting.getMeetingId(),subMeetingId,"【权限校验】主持人"+hostId+"没有生成纪要权限,跳过生成");
continue; continue;
} }
log.info("【权限校验】主持人{}允许生成纪要",hostId); log.info("【权限校验】主持人{}允许生成纪要",hostId);
// processLogService.log(meeting.getMeetingId(),subMeetingId,"【权限校验】主持人"+hostId+"允许生成纪要"); // processLogService.log(meeting.getMeetingId(),subMeetingId,"【权限校验】主持人"+hostId+"允许生成纪要");
UserDTO userDTO = accessUserIds.stream().filter(item -> item.getTid().equals(hostId)).findFirst().get();
email = userDTO.getEmail();
//会议基本信息保存
MeetingInfo meetingItem = MeetingInfo.builder().meetingId(meetingId).meetingCode(meetingInfo.getMeetingCode()) UserDTO userDTO = accessUserIds.stream().filter(item -> item.getTid().equals(hostId)).findFirst().get();
.subject(meetingInfo.getSubject()) email = userDTO.getEmail();
.startTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(Long.valueOf(meetingInfo.getStartTime())), ZoneId.systemDefault()))
.endTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(Long.valueOf(meetingInfo.getEndTime())), ZoneId.systemDefault())) //会议基本信息保存
.isGenerated(Boolean.FALSE).emailPushAccess(Boolean.TRUE).isPushed(Boolean.FALSE).syncTime(LocalDateTime.now()) MeetingInfo meetingItem = MeetingInfo.builder().meetingId(meetingId).meetingCode(meeting.getMeetingCode())
.subMeetingId(subMeetingId).generateRetry(Boolean.FALSE).pushRetry(Boolean.FALSE) .subject(meeting.getSubject())
.host(hostName) .startTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.valueOf(meeting.getMediaStartTime())), ZoneId.systemDefault()))
.hostUid(hostId) // .endTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(Long.valueOf(meeting.getEndTime())), ZoneId.systemDefault()))
.isGenerated(Boolean.FALSE).emailPushAccess(Boolean.TRUE).isPushed(Boolean.FALSE).syncTime(LocalDateTime.now())
.subMeetingId(subMeetingId).generateRetry(Boolean.FALSE).pushRetry(Boolean.FALSE)
.host(hostName)
.hostUid(hostId)
// .participantUsers(participants.stream() // .participantUsers(participants.stream()
// .map(item->new String(Base64.getDecoder().decode(item.getUserName()))).distinct().collect(Collectors.joining("、"))) // .map(item->new String(Base64.getDecoder().decode(item.getUserName()))).distinct().collect(Collectors.joining("、")))
.recordFileId(recordFileIdList.stream().collect(Collectors.joining(","))) .recordFileId(recordFileIdList.stream().collect(Collectors.joining(",")))
.email(email) .email(email)
.build(); .build();
recordFileUrlList.add(recordFileItem); recordFileUrlList.add(recordFileItem);
meetingSaveList.add(meetingItem); meetingSaveList.add(meetingItem);
}
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
......
// service/impl/UserServiceImpl.java
package com.cmeeting.service.impl;
import com.cmeeting.dto.LoginDTO;
import com.cmeeting.mapper.primary.UserMapper;
import com.cmeeting.pojo.User;
import com.cmeeting.service.UserService;
import com.cmeeting.vo.LoginVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public LoginVO login(LoginDTO loginDTO) {
// 1. 验证用户名是否存在
User user = userMapper.findByUsername(loginDTO.getUsername());
if (user == null) {
throw new RuntimeException("用户名或密码错误");
}
// 2. 验证密码
String inputPassword = encryptPassword(loginDTO.getPassword(), user.getSalt());
if (!inputPassword.equals(user.getPassword())) {
throw new RuntimeException("用户名或密码错误");
}
// 3. 生成token (这里简化处理,实际应该用JWT等)
String token = generateToken(user);
// 4. 返回登录结果
LoginVO loginVO = new LoginVO();
loginVO.setUserId(user.getId());
loginVO.setUsername(user.getUsername());
loginVO.setToken(token);
return loginVO;
}
private String encryptPassword(String password, String salt) {
// 实际项目中应该使用更安全的加密方式
return DigestUtils.md5DigestAsHex((password + salt).getBytes());
}
private String generateToken(User user) {
// 简化处理,实际应该用JWT等安全方式生成token
return DigestUtils.md5DigestAsHex((user.getId() + user.getUsername() + System.currentTimeMillis()).getBytes());
}
}
\ No newline at end of file
package com.cmeeting.util;
//import com.google.api.client.repackaged.org.apache.commons.codec.binary.Base64;
import cn.hutool.core.codec.Base64;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
@Slf4j
public class AESUtils {
//// @Value("${aec.key}")
// public static String AES_KEY = "biaopin123456789" ;
/**
* AES加密+Base64转码
*
* @param data 明文(16进制)
* @return
*/
public static String encrypt(String data,String AES_KEY) {
byte[] keyb = null;
try {
keyb = AES_KEY.getBytes("utf-8");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
} // 明文
SecretKeySpec sKeySpec = new SecretKeySpec(keyb, "AES");
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES");
} catch (Exception e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] bjiamihou = null;
String miwen = "";
try {
bjiamihou = cipher.doFinal(data.getBytes("utf-8"));
// byte加密后
miwen = Base64.encode(bjiamihou);// 密文用base64加密
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return miwen;
}
/**
* Base64解码 + AES解码
*
* @param data 密文 (16进制)
* @return
*/
public static String decrypt(String data,String AES_KEY){
byte[] keyb = null;
try {
keyb = AES_KEY.getBytes("utf-8");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
byte[] miwen = Base64.decode(data);
SecretKeySpec sKeySpec = new SecretKeySpec(keyb, "AES");
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES");
} catch (Exception e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] bjiemihou = null;
String mingwen = "";
try {
bjiemihou = cipher.doFinal(miwen);
// byte加密后
mingwen = new String(bjiemihou,"utf-8");
} catch (Exception e) {
e.printStackTrace();
}
return mingwen;
}
public static Boolean decryptSign(HttpServletRequest request, String AES_KEY){
String lid = request.getHeader("lid");
log.info("lid=============" + lid);
String sign = request.getHeader("sign");
log.info("sign=============" +sign);
String decrypt = decrypt(sign, AES_KEY);
log.info("decrypt=============" + decrypt);
if (StringUtils.isEmpty(lid) && StringUtils.isEmpty(sign) && StringUtils.isEmpty(decrypt)){
return true;
}
if(!lid.equals(decrypt)){
log.info("----判定成功");
return true;
}
return false;
}
public static void main(String[] args) throws Exception {
int code = 1;
/*String[] arr = {""};
System.out.println(Arrays.toString(arr));
System.out.println(StringUtils.isEmpty(Arrays.toString(arr)));
System.out.println(StringUtils.isEmpty(arr[0]));*/
// String json = "{'username':'yanghanjing','password':'badou123456','key':'12632740','code':4,'isSaveLogin':false}";
// JSONObject jsonObject = JSONObject.parseObject(json);
// Map<String, Object> innerMap = jsonObject.getInnerMap();
// System.out.println(innerMap.get("username"));
//测试加密 feishu feishu123
// String json = "{\"username\":\"hongdongbao1\",\"password\":\"hongdongbao123\",\"key\":\"08277992\",\"code\":"+code+",\"isSaveLogin\":false}";
String json = "{\"username\":\"ceshi_117\",\"password\":\"ceshi123\"}";
String miwen = encrypt(json,"biaopin123456789");// 加密
System.out.println(miwen);
// 测试解密工具类
/*String data = "ts0oGkerChzxZiOKQCkd/z5Hqvt5g9GXrLsZS0dqgCXIJAGmTgUn/Hfg6ponbjAR2WTk3u8x+VNsFtj8NKrf1hvSV7mjZoOdVHSqU9cx+qbPBi/lhBb+mBKTtuWYhG4HwoPl+NLZmCB18a4susJOfA==";
System.out.println(decrypt(data,"biaopin123456789"));// 解密*/
/*JSONObject ject1 = new JSONObject();
JSONObject ject2 = new JSONObject();
ject1.put("1",1);
ject1.put("2",2);
ject1.put("3",3);
ject2.put("3",3);
ject2.put("1",1);
ject2.put("2",1);
System.out.println(ject1.equals(ject2));*/
}
}
package com.cmeeting.util;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.Charsets;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Slf4j
@Component
public class HttpClientUtils {
private static RequestConfig requestConfig = null;
static {
// 设置请求和传输超时时间
requestConfig = RequestConfig.custom().setSocketTimeout(180000).setConnectTimeout(180000).setConnectionRequestTimeout(180000).build();
}
/**
* 发送get请求
*
* @param url 路径
* @return
*/
public static JSONObject httpGet(String url) {
log.info("请求地址:【{}】", url);
// get请求返回结果
JSONObject jsonResult = null;
CloseableHttpClient client = HttpClients.createDefault();
// 发送get请求
HttpGet request = new HttpGet(url);
request.setConfig(requestConfig);
try {
CloseableHttpResponse response = client.execute(request);
// 请求发送成功,并得到响应
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// 读取服务器返回过来的json字符串数据
HttpEntity entity = response.getEntity();
String strResult = EntityUtils.toString(entity, "utf-8");
// 把json字符串转换成json对象
jsonResult = JSONObject.parseObject(strResult);
log.info("请求返回结果:【{}】", jsonResult.toJSONString());
} else {
log.error("get请求提交失败:" + url);
}
} catch (IOException e) {
log.error("get请求提交失败:" + url, e);
} finally {
request.releaseConnection();
}
return jsonResult;
}
public static JSONObject httpGet(String url, String token) {
log.info("请求地址:【{}】", url);
// get请求返回结果
JSONObject jsonResult = null;
CloseableHttpClient client = HttpClients.createDefault();
// 发送get请求
HttpGet request = new HttpGet(url);
if (token != null) {
request.setHeader("Authorization", "Bearer " + token);
}
request.setConfig(requestConfig);
try {
CloseableHttpResponse response = client.execute(request);
// 请求发送成功,并得到响应
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// 读取服务器返回过来的json字符串数据
HttpEntity entity = response.getEntity();
String strResult = EntityUtils.toString(entity, "utf-8");
// 把json字符串转换成json对象
jsonResult = JSONObject.parseObject(strResult);
log.info("请求返回结果:【{}】", jsonResult.toJSONString());
} else {
log.error("get请求提交失败:" + url);
}
} catch (IOException e) {
log.error("get请求提交失败:" + url, e);
} finally {
request.releaseConnection();
}
return jsonResult;
}
public static JSONObject httpGet(String url, String token, List<Header> headers, boolean isSafe) {
log.info("请求地址:【{}】", url);
// get请求返回结果
JSONObject jsonResult = null;
// 发送get请求
HttpGet request = new HttpGet(url);
if (token != null) {
request.setHeader("Authorization", "Bearer " + token);
}
// 设置头参数
if (CollectionUtils.isNotEmpty(headers)) {
for (Header item : headers) {
request.setHeader(item.getName(), item.getValue());
log.info("请求Header:{}={}", item.getName(), item.getValue());
}
}
request.setConfig(requestConfig);
try {
CloseableHttpClient client = null;
if(isSafe){
client = HttpClientBuilder.create().build();
}else{
// 创建一个信任所有证书的SSLContext
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true; // 忽略所有证书验证
}
})
.build();
client = HttpClientBuilder.create().setSSLContext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
}
CloseableHttpResponse response = client.execute(request);
// 请求发送成功,并得到响应
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// 读取服务器返回过来的json字符串数据
HttpEntity entity = response.getEntity();
String strResult = EntityUtils.toString(entity, "utf-8");
// 把json字符串转换成json对象
jsonResult = JSONObject.parseObject(strResult);
log.info("请求返回结果:【{}】", jsonResult.toJSONString());
} else {
log.error("get请求提交失败:{},code:{}" ,url,response.getStatusLine().getStatusCode());
}
} catch (IOException | NoSuchAlgorithmException | KeyStoreException e) {
log.error("get请求提交失败:" + url, e);
} catch (KeyManagementException e) {
throw new RuntimeException(e);
} finally {
request.releaseConnection();
}
return jsonResult;
}
/**
* get请求,参数放在map里
*
* @param url 请求地址
* @param map 参数map
* @return 响应
*/
public static JSONObject getMap(String url, Map<String, String> map) {
log.info(url + "getMap-start :" + System.currentTimeMillis());
log.info("请求地址:【{}】", url);
CloseableHttpClient httpClient = HttpClients.createDefault();
List<NameValuePair> pairs = new ArrayList<>();
for (Map.Entry<String, String> entry : map.entrySet()) {
pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
CloseableHttpResponse response = null;
try {
URIBuilder builder = new URIBuilder(url);
builder.setParameters(pairs);
HttpGet get = new HttpGet(builder.build());
response = httpClient.execute(get);
log.info(url + "getMap-end :" + System.currentTimeMillis());
HttpEntity entity = response.getEntity();
String resultJson = EntityUtils.toString(entity);
log.info("请求返回的原始数据:【{}】", resultJson);
JSONObject result = JSONObject.parseObject(resultJson);
if (response != null && response.getStatusLine().getStatusCode() == 200) {
log.info("返回结果:【{}】", result.toJSONString());
} else {
log.info("返回结果异常:【{}】", result.toJSONString());
}
return result;
} catch (URISyntaxException e) {
log.error("请求失败", e);
} catch (ClientProtocolException e) {
log.error("请求失败", e);
} catch (IOException e) {
log.error("请求失败", e);
} finally {
try {
httpClient.close();
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public static JSONObject postForForm(String url, Map<String, String> parms) {
log.info("请求地址:{}", url);
HttpPost httpPost = new HttpPost(url);
ArrayList<BasicNameValuePair> list = new ArrayList<>();
parms.forEach((key, value) -> list.add(new BasicNameValuePair(key, value)));
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
if (Objects.nonNull(parms) && parms.size() > 0) {
httpPost.setEntity(new UrlEncodedFormEntity(list, "UTF-8"));
}
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
JSONObject jsonObject = JSON.parseObject(EntityUtils.toString(entity, "UTF-8"));
log.info("请求结果:{}", jsonObject.toJSONString());
return jsonObject;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (Objects.nonNull(httpClient)) {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
/**
* get请求(用于key-value格式的参数,且带有header请求头)
*
* @param url
* @param map 请求的参数map<key,value>键值对
* @return
*/
public static String doGet(String url, Map<String, String> map) {
log.info("请求地址:【{}】", url);
// 获取连接客户端工具
CloseableHttpClient httpClient = HttpClients.createDefault();
String entityStr = null;
CloseableHttpResponse response = null;
try {
/*
* 由于GET请求的参数都是拼装在URL地址后方,所以我们要构建一个URL,带参数
*/
URIBuilder uriBuilder = new URIBuilder(url);
/** 第一种添加参数的形式 */
/*uriBuilder.addParameter("name", "root");
uriBuilder.addParameter("password", "123456");*/
for (Map.Entry<String, String> entry : map.entrySet()) {
uriBuilder.addParameter(entry.getKey(), entry.getValue());
}
/** 第二种添加参数的形式 */
/* List<NameValuePair> list = new LinkedList<>();
BasicNameValuePair param1 = new BasicNameValuePair("startTime", "2019-1-1 00:00:00");
BasicNameValuePair param2 = new BasicNameValuePair("endTime", "2019-1-9 00:00:00");
list.add(param1);
list.add(param2);
uriBuilder.setParameters(list);*/
// 根据带参数的URI对象构建GET请求对象
HttpGet httpGet = new HttpGet(uriBuilder.build());
/*
* 添加请求头信息
*/
// 浏览器表示
httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)");
// 传输的类型
httpGet.addHeader("Content-Type", "application/x-www-form-urlencoded");
//header的<key,value>
//httpGet.setHeader(headerKey, headerValue);
// 执行请求
response = httpClient.execute(httpGet);
// 获得响应的实体对象
HttpEntity entity = response.getEntity();
// 使用Apache提供的工具类进行转换成字符串
entityStr = EntityUtils.toString(entity, "UTF-8");
log.info("返回结果:【{}】", entityStr);
} catch (ClientProtocolException e) {
log.error("Http协议出现问题", e);
} catch (ParseException e) {
log.error("解析错误", e);
} catch (URISyntaxException e) {
log.error("URI解析异常", e);
} catch (IOException e) {
log.error("IO异常", e);
} finally {
// 释放连接
if (null != response) {
try {
response.close();
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return entityStr;
}
/**
* post请求传输json参数
*
* @param url url地址
* @return
*/
public static JSONObject httpPost(String url, cn.hutool.json.JSONObject jsonParam, String token) {
// post请求返回结果
CloseableHttpClient httpClient = HttpClients.createDefault();
JSONObject jsonResult = null;
HttpPost httpPost = new HttpPost(url);
// 设置请求和传输超时时间
httpPost.setConfig(requestConfig);
if (token != null) {
httpPost.setHeader("Authorization", "Bearer " + token);
}
try {
if (null != jsonParam) {
// 解决中文乱码问题
StringEntity entity = new StringEntity(jsonParam.toString(), "utf-8");
entity.setContentEncoding("UTF-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
}
CloseableHttpResponse result = httpClient.execute(httpPost);
// 请求发送成功,并得到响应
if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String str = "";
try {
// 读取服务器返回过来的json字符串数据
str = EntityUtils.toString(result.getEntity(), "utf-8");
// 把json字符串转换成json对象
jsonResult = JSONObject.parseObject(str);
} catch (Exception e) {
log.error("post请求提交失败:" + url, e);
}
}
} catch (IOException e) {
log.error("post请求提交失败:" + url, e);
} finally {
httpPost.releaseConnection();
}
return jsonResult;
}
public static JSONObject httpPost(String url, JSONObject jsonParam, String token) {
// post请求返回结果
CloseableHttpClient httpClient = HttpClients.createDefault();
JSONObject jsonResult = null;
HttpPost httpPost = new HttpPost(url);
// 设置请求和传输超时时间
httpPost.setConfig(requestConfig);
if (token != null) {
httpPost.setHeader("Authorization", "Bearer " + token);
}
try {
if (null != jsonParam) {
// 解决中文乱码问题
StringEntity entity = new StringEntity(jsonParam.toString(), "utf-8");
entity.setContentEncoding("UTF-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
}
CloseableHttpResponse result = httpClient.execute(httpPost);
// 请求发送成功,并得到响应
if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String str = "";
try {
// 读取服务器返回过来的json字符串数据
str = EntityUtils.toString(result.getEntity(), "utf-8");
// 把json字符串转换成json对象
jsonResult = JSONObject.parseObject(str);
} catch (Exception e) {
log.error("post请求提交失败:" + url, e);
}
}
} catch (IOException e) {
log.error("post请求提交失败:" + url, e);
} finally {
httpPost.releaseConnection();
}
return jsonResult;
}
/**
* post请求传输String参数 例如:name=Jack&sex=1&type=2
* Content-type:application/x-www-form-urlencoded
*
* @param url url地址
* @param strParam 参数
* @return
*/
public static JSONObject httpPost(String url, String strParam) {
// post请求返回结果
CloseableHttpClient httpClient = HttpClients.createDefault();
JSONObject jsonResult = null;
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(requestConfig);
try {
if (null != strParam) {
// 解决中文乱码问题
StringEntity entity = new StringEntity(strParam, "utf-8");
entity.setContentEncoding("UTF-8");
entity.setContentType("application/x-www-form-urlencoded");
httpPost.setEntity(entity);
}
CloseableHttpResponse result = httpClient.execute(httpPost);
// 请求发送成功,并得到响应
if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String str = "";
try {
// 读取服务器返回过来的json字符串数据
str = EntityUtils.toString(result.getEntity(), "utf-8");
// 把json字符串转换成json对象
jsonResult = JSONObject.parseObject(str);
} catch (Exception e) {
log.error("post请求提交失败:" + url, e);
}
}
} catch (IOException e) {
log.error("post请求提交失败:" + url, e);
} finally {
httpPost.releaseConnection();
}
return jsonResult;
}
/**
* 发送POST请求
*
* @param url
* @param header
* @param parametersBody
* @param timeout
* @return
*/
public static JSONObject postRequest(String url, List<Header> header, String parametersBody, Integer timeout) {
log.info("请求地址:{}", url);
log.info("请求方式:POST");
HttpPost post = new HttpPost(url);
post.addHeader("Content-Type", "application/json;charset=UTF-8");
post.addHeader("Accept", "application/json");
// 设置头参数
if (CollectionUtils.isNotEmpty(header)) {
for (Header item : header) {
post.setHeader(item.getName(), item.getValue());
log.info("请求Header:{}={}", item.getName(), item.getValue());
}
}
try {
StringEntity entity = new StringEntity(parametersBody);
post.setEntity(entity);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 请求参数
if (timeout != null && timeout.intValue() > 0) {
RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout).setConnectionRequestTimeout(timeout).setSocketTimeout(timeout).build();
post.setConfig(config);
}
try {
log.info("请求参数:{}", parametersBody);
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(post);
int code = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
String s = EntityUtils.toString(entity);
JSONObject result = (JSONObject) JSONObject.parseObject(s);
if (code >= 400) {
throw new RuntimeException(result.toJSONString());
}
log.info("请求返回结果:{}", result);
return result;
} catch (ClientProtocolException e) {
throw new RuntimeException("postRequest -- Client protocol exception!", e);
} catch (IOException e) {
throw new RuntimeException("postRequest -- IO error!", e);
} finally {
post.releaseConnection();
}
}
/**
* 发送POST请求
*
* @param url
* @param headerParams
* @param parametersBody
* @param timeout
* @return
*/
public static JSONObject postJsonRequest(String url, Map<String, String> headerParams, String parametersBody, Integer timeout) {
log.info("请求地址:{}", url);
log.info("请求方式:POST");
HttpPost post = new HttpPost(url);
post.addHeader("Content-Type", "text/plain");
// 设置头参数
//设置请求头
if (headerParams != null && headerParams.size() > 0) {
for (Map.Entry<String, String> e : headerParams.entrySet()) {
String value = e.getValue();
String key = e.getKey();
if (StrUtil.isNotBlank(value)) {
post.setHeader(key, value);
}
}
}
try {
StringEntity entity = new StringEntity(parametersBody);
entity.setContentType("text/plain");
entity.setContentEncoding("UTF-8");
post.setEntity(entity);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 请求参数
if (timeout != null && timeout.intValue() > 0) {
RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout).setConnectionRequestTimeout(timeout).setSocketTimeout(timeout).build();
post.setConfig(config);
}
try {
log.info("请求参数:{}", parametersBody);
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(post);
int code = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
String s = EntityUtils.toString(entity);
JSONObject result = (JSONObject) JSONObject.parseObject(s);
if (code >= 400) {
throw new RuntimeException(result.toJSONString());
}
log.info("请求返回结果:{}", result);
return result;
} catch (ClientProtocolException e) {
throw new RuntimeException("postRequest -- Client protocol exception!", e);
} catch (IOException e) {
throw new RuntimeException("postRequest -- IO error!", e);
} finally {
post.releaseConnection();
}
}
/**
* get请求,参数放在map里
*
* @param url 请求地址
* @param map 参数map
* @return 响应
*/
public static String getfileMap(String url, Map<String, String> map, String fileUrl, String fileName) {
CloseableHttpClient httpClient = HttpClients.createDefault();
List<NameValuePair> pairs = new ArrayList<>();
for (Map.Entry<String, String> entry : map.entrySet()) {
pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
CloseableHttpResponse response = null;
String filestr = null;
InputStream inputStream = null;
try {
URIBuilder builder = new URIBuilder(url);
builder.setParameters(pairs);
HttpGet get = new HttpGet(builder.build());
response = httpClient.execute(get);
if (response != null && response.getStatusLine().getStatusCode() == 200) {
inputStream = response.getEntity().getContent();
Header[] headers1 = response.getHeaders("Content-disposition");/* for (int i = 0; i < headers1.length; i++) {
Header header = headers1[0];
String filenameheader = header.getValue();
String[] strings = filenameheader.split(";");
String strs = strings[1];
String str = strs.substring(11, strs.length() - 1);
String[] spstr = str.split("\\.");
String fileend = spstr[1];
log.info("fileend = " + fileend);
}*/
filestr = fileUrl + fileName + ".silk";
File file1 = new File(filestr);
if (!file1.exists()) {
file1.createNewFile();
}
OutputStream os = new FileOutputStream(file1);
int read = 0;
byte[] temp = new byte[1024 * 1024];
while ((read = inputStream.read(temp)) > 0) {
byte[] bytes = new byte[read];
System.arraycopy(temp, 0, bytes, 0, read);
os.write(bytes);
}
os.flush();
}
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
httpClient.close();
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return filestr;
}
/**
* 发送POST请求(普通表单形式)
*
* @param url
* @param header
* @param parametersBody
* @param timeout
* @return
*/
public static String postForm(String url, List<Header> header, List<BasicNameValuePair> parametersBody, Integer timeout) {
HttpEntity entity = new UrlEncodedFormEntity(parametersBody, Charsets.UTF_8);
return postRequest(url, "application/x-www-form-urlencoded", header, entity, timeout, true);
}
/**
* 发送POST请求(JSON形式)
*
* @param url
* @param header 请求header
* @param json 请求body,json格式
* @param timeout 访问超时时间,单位毫秒
* @return
*/
public static String postJSON(String url, List<Header> header, String json, Integer timeout, boolean isSafe) {
StringEntity entity = new StringEntity(json, Charsets.UTF_8);
return postRequest(url, "application/json; charset=utf-8", header, entity, timeout, isSafe);
}
/**
* 发送POST请求
*
* @param url
* @param mediaType
* @param header
* @param parametersBody
* @param timeout
* @return
*/
private static String postRequest(String url, String mediaType, List<Header> header, HttpEntity parametersBody, Integer timeout, boolean isSafe) {
log.info("请求地址:{}", url);
log.info("请求方式:POST");
HttpPost post = new HttpPost(url);
post.addHeader("Content-Type", mediaType);
post.addHeader("Accept", "application/json");
// 设置头参数
if (CollectionUtils.isNotEmpty(header)) {
for (Header item : header) {
post.setHeader(item.getName(), item.getValue());
log.info("请求Header:{}={}", item.getName(), item.getValue());
}
}
// 请求参数
post.setEntity(parametersBody);
if (timeout != null && timeout.intValue() > 0) {
RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout).setConnectionRequestTimeout(timeout).setSocketTimeout(timeout).build();
post.setConfig(config);
}
try {
log.info("请求参数:{}", EntityUtils.toString(parametersBody));
CloseableHttpClient client = null;
if(isSafe){
client = HttpClientBuilder.create().build();
}else{
// 创建一个信任所有证书的SSLContext
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true; // 忽略所有证书验证
}
})
.build();
client = HttpClientBuilder.create().setSSLContext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
}
HttpResponse response = client.execute(post);
int code = response.getStatusLine().getStatusCode();
String result = EntityUtils.toString(response.getEntity());
if (code >= 400) {
throw new RuntimeException(result);
}
log.info("请求返回结果:{}", result);
return result;
} catch (ClientProtocolException e) {
throw new RuntimeException("postRequest -- Client protocol exception!", e);
} catch (IOException e) {
throw new RuntimeException("postRequest -- IO error!", e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (KeyStoreException e) {
throw new RuntimeException(e);
} catch (KeyManagementException e) {
throw new RuntimeException(e);
} finally {
post.releaseConnection();
}
}
}
package com.cmeeting.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 获取Ip
*/
public class IPUtils {
private static final String IP_UTILS_FLAG = ",";
private static final String UNKNOWN = "unknown";
private static final String LOCALHOST_IP = "0:0:0:0:0:0:0:1";
private static final String LOCALHOST_IP1 = "127.0.0.1";
private static Logger logger = LoggerFactory.getLogger(IPUtils.class);
/**
* 获取IP地址
* <p>
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = null;
try {
//以下两个获取在k8s中,将真实的客户端IP,放到了x-Original-Forwarded-For。而将WAF的回源地址放到了 x-Forwarded-For了。
ip = request.getHeader("X-Original-Forwarded-For");
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
//获取nginx等代理的ip
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("x-forwarded-for");
}
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
//兼容k8s集群获取ip
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) {
//根据网卡取本机配置的IP
InetAddress iNet = null;
try {
iNet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
logger.error("getClientIp error: {}", e);
}
ip = iNet.getHostAddress();
}
}
} catch (Exception e) {
logger.error("IPUtils ERROR ", e);
}
//使用代理,则获取第一个IP地址
if (!StringUtils.isEmpty(ip) && ip.indexOf(IP_UTILS_FLAG) > 0) {
ip = ip.substring(0, ip.indexOf(IP_UTILS_FLAG));
}
return ip;
}
}
\ No newline at end of file
package com.cmeeting.util;
import java.net.URLEncoder;
/**
* @author liuzhenmeng
* @date 2024/8/30 13:41
*/
public class UrlEncoderUtil {
public static String encode(String param){
try {
return URLEncoder.encode(param, "UTF-8");
} catch (Exception e){
e.printStackTrace();
}
return param;
}
}
package com.cmeeting.util;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component
public class UserAdminConfig {
@Value("${userAdmin.domain}")
private String userAdminDomain;
}
package com.cmeeting.util;
import com.alibaba.fastjson.JSONObject;
import com.cmeeting.constant.UserAdminRouteConstant;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
* @author a
*/
@Component
public class UserAdminTokenUtil {
@Resource
private RedisUtils redisUtils;
@Resource
private UserAdminConfig userAdminConfig;
@Value("${userAdmin.appId}")
private String appId;
@Value("${userAdmin.secret}")
private String secret;
private String getAccessToken() {
String key = "user_admin_token_" + appId;
Object o = redisUtils.get(key);
if (o != null) {
return o.toString();
}
String url = userAdminConfig.getUserAdminDomain() + UserAdminRouteConstant.ACCESS_TOKEN;
JSONObject body = new JSONObject();
body.put("appId", appId);
body.put("secret", secret);
JSONObject object = HttpClientUtils.httpPost(url, body, null);
if (object != null && object.getInteger("code") == 0) {
String data = object.getString("data");
redisUtils.set(key, data, 7000);
return data;
} else {
throw new RuntimeException("获取accessToken失败");
}
}
private static UserAdminTokenUtil util;
@PostConstruct
public void init() {
util = this;
}
public static String getUserAdminToken() {
return util.getAccessToken();
}
}
...@@ -26,7 +26,7 @@ public class CorpRecordsVO { ...@@ -26,7 +26,7 @@ public class CorpRecordsVO {
private String meetingCode; private String meetingCode;
private String userid; private String userid;
private String hostUserId; private String hostUserId;
private Integer mediaStartTime; private Long mediaStartTime;
private String subject; private String subject;
private Integer state; private Integer state;
private Integer recordType; private Integer recordType;
...@@ -36,8 +36,8 @@ public class CorpRecordsVO { ...@@ -36,8 +36,8 @@ public class CorpRecordsVO {
@Data @Data
public class RecordFile { public class RecordFile {
private String recordFileId; private String recordFileId;
private Integer recordStartTime; private Long recordStartTime;
private Integer recordEndTime; private Long recordEndTime;
private Integer recordSize; private Integer recordSize;
private Integer sharingState; private Integer sharingState;
private String sharingUrl; private String sharingUrl;
......
...@@ -2,6 +2,8 @@ package com.cmeeting.vo; ...@@ -2,6 +2,8 @@ package com.cmeeting.vo;
import lombok.Data; import lombok.Data;
import java.util.List;
/** /**
* 会议纪要模板展示层 * 会议纪要模板展示层
*/ */
...@@ -42,4 +44,29 @@ public class RecordTemplateVO { ...@@ -42,4 +44,29 @@ public class RecordTemplateVO {
* 每页最大数据行 * 每页最大数据行
*/ */
private Integer size; private Integer size;
@Data
public static class TemplatePermissionVO{
/**
* 模板id
*/
private Integer templateId;
/**
* 选中的部门或人员
*/
private List<PermissionItem> permissionItems;
}
@Data
public static class PermissionItem{
/**
* 对象类型
*/
private String relType;
/**
* 关联对象id
*/
private String relId;
}
} }
\ No newline at end of file
...@@ -104,7 +104,7 @@ tencent: ...@@ -104,7 +104,7 @@ tencent:
meeting: meeting:
token: QQZNb7xWQB47MpZF4C2DFAkv8 token: QQZNb7xWQB47MpZF4C2DFAkv8
aesKey: agy6ALUePp34lljWz1uIQWa7yQq3dgxxQNmfaN9GROm aesKey: agy6ALUePp34lljWz1uIQWa7yQq3dgxxQNmfaN9GROm
base-save-path: /save/ base-save-path: E:/save/
email: email:
sender: ${EMAIL_SENDER} sender: ${EMAIL_SENDER}
...@@ -114,6 +114,34 @@ email: ...@@ -114,6 +114,34 @@ email:
llm: llm:
api-addr: ${LLM_API_ADDR} api-addr: ${LLM_API_ADDR}
#解密key
aec:
key: biaopin123456789
ldap:
url: 'ldap://10.52.3.20/'
base: 'CN=AIGC,OU=数据网络中心,OU=集团公司,OU=集团总部,OU=无HR信息人员,OU=特殊邮箱,DC=cimc,DC=com'
adName: 'AIGC@cimc.com'
adPwd: '32Rgn87#8@dbc'
referral: 'follow'
domain: '@cimc.com'
userAdmin:
domain: http://192.168.10.154:8303
appId: af94e0ec9cc0bef2bfa63f470f6f4351
secret: 4luHTMYMyXzOmDHN0ZOSMwOst702plcr9EKqAHtT8Nag6EPlP0VSFvqv9Zq0NRDE
removeMenus: 关键词回复,业务词库,同义词管理,答案库
jwt:
header: Authorization
customHeader: 'Custom-Auth-Token'
applicationId: 'Application-Id'
tokenHead: 'Bearer '
openingUp-header: 'aigc-open-auth-token'
expireTime: 36000
customExpireTime: 36000 # 5天
permission: permission:
applicationId: ${PERMISSION_APPLiCATION_ID} applicationId: ${PERMISSION_APPLiCATION_ID}
tenantId: ${PERMISSION_TENANT_ID} tenantId: ${PERMISSION_TENANT_ID}
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC
"-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmeeting.mapper.primary.CommonMapper">
<select id="getMenuList" resultType="com.cmeeting.ad.entity.SysMenu">
select t1.*
from sys_menu t1
<if test="tenantId != '' and tenantId != 1">
inner join rel_menu_tenant t2 on t2.menu_id = t1.id and t2.tenant_id = #{tenantId}
</if>
where 1 = 1
<choose>
<when test="tenantId != '' and tenantId == 1">
and is_admin = 1
</when>
<otherwise>
and is_admin = 0
</otherwise>
</choose>
</select>
<select id="getRoleByTenantId" resultType="com.cmeeting.ad.entity.ModulePermissions">
select *
from core_module_permissions
where tenant_id = #{tenantId}
and source = 0;
</select>
<select id="getMenuByRoleList" resultType="com.cmeeting.ad.entity.SysMenu">
select distinct t1.*
from sys_menu t1
inner join rel_role_menu t2
on t1.id = t2.menu_id
<if test="tenantId!=null and tenantId!=''">
and t2.tenant_id = #{tenantId}
</if>
<if test="roleIdList!= null">
and role_id in
<foreach collection="roleIdList" close=")" open="(" separator="," item="id">
#{id}
</foreach>
</if>
inner join sys_role t3
on t3.disabled = 0
<if test="tenantId!=null and tenantId!=''">
and t3.tenant_id = #{tenantId}
</if>
</select>
<select id="getApplicationNoTenantId" resultType="com.cmeeting.ad.vo.ApplicationVo">
SELECT
t1.id as applicationId,
t1.`name` as applicationName,
t1.tenant_id as tenantId
FROM
assistant_info t1
where t1.is_del = 0
</select>
<select id="getSubDeptId" resultType="java.lang.String">
SELECT dept_id
FROM sys_user_sync_category
WHERE parent_id = #{deptId}
</select>
<select id="getUsersByDept" resultType="java.lang.String">
select t1.user_id as wId
from sys_user_sync t1
where t1.dept_id in
<foreach collection="deptIds" item="deptId" separator="," open="(" close=")">
#{deptId}
</foreach>
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC
"-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmeeting.mapper.primary.RecordTemplatePermissionMapper">
<insert id="batchInsert">
INSERT IGNORE INTO cmt_record_template_permission (template_id, template_type, rel_type, rel_id, create_time)
VALUES
<foreach collection="permissions" item="permission" separator=",">
(
#{permission.templateId},
#{permission.template_type},
#{permission.relType},
#{permission.relId},
#{permission.createTime}
)
</foreach>
</insert>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC
"-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmeeting.mapper.primary.SysTenantMapper">
</mapper>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论