feat(系统设置): 增加邮箱邀请用户功能

增加邮箱邀请用户功能
This commit is contained in:
song-tianyang 2023-09-04 17:51:21 +08:00 committed by 刘瑞斌
parent 8e9e01a8b4
commit 4b6d848577
27 changed files with 1715 additions and 62 deletions

View File

@ -0,0 +1,111 @@
package io.metersphere.system.domain;
import io.metersphere.validation.groups.*;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import lombok.Data;
@Data
public class UserInvite implements Serializable {
@Schema(title = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{user_invite.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{user_invite.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(title = "邀请邮箱", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{user_invite.email.not_blank}", groups = {Created.class})
@Size(min = 1, max = 255, message = "{user_invite.email.length_range}", groups = {Created.class, Updated.class})
private String email;
@Schema(title = "邀请用户", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{user_invite.invite_user.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{user_invite.invite_user.length_range}", groups = {Created.class, Updated.class})
private String inviteUser;
@Schema(title = "邀请时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{user_invite.invite_time.not_blank}", groups = {Created.class})
private Long inviteTime;
@Schema(title = "所属权限")
private String roles;
private static final long serialVersionUID = 1L;
public enum Column {
id("id", "id", "VARCHAR", false),
email("email", "email", "VARCHAR", false),
inviteUser("invite_user", "inviteUser", "VARCHAR", false),
inviteTime("invite_time", "inviteTime", "BIGINT", false),
roles("roles", "roles", "LONGVARCHAR", false);
private static final String BEGINNING_DELIMITER = "`";
private static final String ENDING_DELIMITER = "`";
private final String column;
private final boolean isColumnNameDelimited;
private final String javaProperty;
private final String jdbcType;
public String value() {
return this.column;
}
public String getValue() {
return this.column;
}
public String getJavaProperty() {
return this.javaProperty;
}
public String getJdbcType() {
return this.jdbcType;
}
Column(String column, String javaProperty, String jdbcType, boolean isColumnNameDelimited) {
this.column = column;
this.javaProperty = javaProperty;
this.jdbcType = jdbcType;
this.isColumnNameDelimited = isColumnNameDelimited;
}
public String desc() {
return this.getEscapedColumnName() + " DESC";
}
public String asc() {
return this.getEscapedColumnName() + " ASC";
}
public static Column[] excludes(Column ... excludes) {
ArrayList<Column> columns = new ArrayList<>(Arrays.asList(Column.values()));
if (excludes != null && excludes.length > 0) {
columns.removeAll(new ArrayList<>(Arrays.asList(excludes)));
}
return columns.toArray(new Column[]{});
}
public static Column[] all() {
return Column.values();
}
public String getEscapedColumnName() {
if (this.isColumnNameDelimited) {
return new StringBuilder().append(BEGINNING_DELIMITER).append(this.column).append(ENDING_DELIMITER).toString();
} else {
return this.column;
}
}
public String getAliasedEscapedColumnName() {
return this.getEscapedColumnName();
}
}
}

View File

@ -0,0 +1,470 @@
package io.metersphere.system.domain;
import java.util.ArrayList;
import java.util.List;
public class UserInviteExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public UserInviteExample() {
oredCriteria = new ArrayList<Criteria>();
}
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
public String getOrderByClause() {
return orderByClause;
}
public void setDistinct(boolean distinct) {
this.distinct = distinct;
}
public boolean isDistinct() {
return distinct;
}
public List<Criteria> getOredCriteria() {
return oredCriteria;
}
public void or(Criteria criteria) {
oredCriteria.add(criteria);
}
public Criteria or() {
Criteria criteria = createCriteriaInternal();
oredCriteria.add(criteria);
return criteria;
}
public Criteria createCriteria() {
Criteria criteria = createCriteriaInternal();
if (oredCriteria.size() == 0) {
oredCriteria.add(criteria);
}
return criteria;
}
protected Criteria createCriteriaInternal() {
Criteria criteria = new Criteria();
return criteria;
}
public void clear() {
oredCriteria.clear();
orderByClause = null;
distinct = false;
}
protected abstract static class GeneratedCriteria {
protected List<Criterion> criteria;
protected GeneratedCriteria() {
super();
criteria = new ArrayList<Criterion>();
}
public boolean isValid() {
return criteria.size() > 0;
}
public List<Criterion> getAllCriteria() {
return criteria;
}
public List<Criterion> getCriteria() {
return criteria;
}
protected void addCriterion(String condition) {
if (condition == null) {
throw new RuntimeException("Value for condition cannot be null");
}
criteria.add(new Criterion(condition));
}
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
throw new RuntimeException("Value for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value));
}
protected void addCriterion(String condition, Object value1, Object value2, String property) {
if (value1 == null || value2 == null) {
throw new RuntimeException("Between values for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value1, value2));
}
public Criteria andIdIsNull() {
addCriterion("id is null");
return (Criteria) this;
}
public Criteria andIdIsNotNull() {
addCriterion("id is not null");
return (Criteria) this;
}
public Criteria andIdEqualTo(String value) {
addCriterion("id =", value, "id");
return (Criteria) this;
}
public Criteria andIdNotEqualTo(String value) {
addCriterion("id <>", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThan(String value) {
addCriterion("id >", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThanOrEqualTo(String value) {
addCriterion("id >=", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThan(String value) {
addCriterion("id <", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThanOrEqualTo(String value) {
addCriterion("id <=", value, "id");
return (Criteria) this;
}
public Criteria andIdLike(String value) {
addCriterion("id like", value, "id");
return (Criteria) this;
}
public Criteria andIdNotLike(String value) {
addCriterion("id not like", value, "id");
return (Criteria) this;
}
public Criteria andIdIn(List<String> values) {
addCriterion("id in", values, "id");
return (Criteria) this;
}
public Criteria andIdNotIn(List<String> values) {
addCriterion("id not in", values, "id");
return (Criteria) this;
}
public Criteria andIdBetween(String value1, String value2) {
addCriterion("id between", value1, value2, "id");
return (Criteria) this;
}
public Criteria andIdNotBetween(String value1, String value2) {
addCriterion("id not between", value1, value2, "id");
return (Criteria) this;
}
public Criteria andEmailIsNull() {
addCriterion("email is null");
return (Criteria) this;
}
public Criteria andEmailIsNotNull() {
addCriterion("email is not null");
return (Criteria) this;
}
public Criteria andEmailEqualTo(String value) {
addCriterion("email =", value, "email");
return (Criteria) this;
}
public Criteria andEmailNotEqualTo(String value) {
addCriterion("email <>", value, "email");
return (Criteria) this;
}
public Criteria andEmailGreaterThan(String value) {
addCriterion("email >", value, "email");
return (Criteria) this;
}
public Criteria andEmailGreaterThanOrEqualTo(String value) {
addCriterion("email >=", value, "email");
return (Criteria) this;
}
public Criteria andEmailLessThan(String value) {
addCriterion("email <", value, "email");
return (Criteria) this;
}
public Criteria andEmailLessThanOrEqualTo(String value) {
addCriterion("email <=", value, "email");
return (Criteria) this;
}
public Criteria andEmailLike(String value) {
addCriterion("email like", value, "email");
return (Criteria) this;
}
public Criteria andEmailNotLike(String value) {
addCriterion("email not like", value, "email");
return (Criteria) this;
}
public Criteria andEmailIn(List<String> values) {
addCriterion("email in", values, "email");
return (Criteria) this;
}
public Criteria andEmailNotIn(List<String> values) {
addCriterion("email not in", values, "email");
return (Criteria) this;
}
public Criteria andEmailBetween(String value1, String value2) {
addCriterion("email between", value1, value2, "email");
return (Criteria) this;
}
public Criteria andEmailNotBetween(String value1, String value2) {
addCriterion("email not between", value1, value2, "email");
return (Criteria) this;
}
public Criteria andInviteUserIsNull() {
addCriterion("invite_user is null");
return (Criteria) this;
}
public Criteria andInviteUserIsNotNull() {
addCriterion("invite_user is not null");
return (Criteria) this;
}
public Criteria andInviteUserEqualTo(String value) {
addCriterion("invite_user =", value, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserNotEqualTo(String value) {
addCriterion("invite_user <>", value, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserGreaterThan(String value) {
addCriterion("invite_user >", value, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserGreaterThanOrEqualTo(String value) {
addCriterion("invite_user >=", value, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserLessThan(String value) {
addCriterion("invite_user <", value, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserLessThanOrEqualTo(String value) {
addCriterion("invite_user <=", value, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserLike(String value) {
addCriterion("invite_user like", value, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserNotLike(String value) {
addCriterion("invite_user not like", value, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserIn(List<String> values) {
addCriterion("invite_user in", values, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserNotIn(List<String> values) {
addCriterion("invite_user not in", values, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserBetween(String value1, String value2) {
addCriterion("invite_user between", value1, value2, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteUserNotBetween(String value1, String value2) {
addCriterion("invite_user not between", value1, value2, "inviteUser");
return (Criteria) this;
}
public Criteria andInviteTimeIsNull() {
addCriterion("invite_time is null");
return (Criteria) this;
}
public Criteria andInviteTimeIsNotNull() {
addCriterion("invite_time is not null");
return (Criteria) this;
}
public Criteria andInviteTimeEqualTo(Long value) {
addCriterion("invite_time =", value, "inviteTime");
return (Criteria) this;
}
public Criteria andInviteTimeNotEqualTo(Long value) {
addCriterion("invite_time <>", value, "inviteTime");
return (Criteria) this;
}
public Criteria andInviteTimeGreaterThan(Long value) {
addCriterion("invite_time >", value, "inviteTime");
return (Criteria) this;
}
public Criteria andInviteTimeGreaterThanOrEqualTo(Long value) {
addCriterion("invite_time >=", value, "inviteTime");
return (Criteria) this;
}
public Criteria andInviteTimeLessThan(Long value) {
addCriterion("invite_time <", value, "inviteTime");
return (Criteria) this;
}
public Criteria andInviteTimeLessThanOrEqualTo(Long value) {
addCriterion("invite_time <=", value, "inviteTime");
return (Criteria) this;
}
public Criteria andInviteTimeIn(List<Long> values) {
addCriterion("invite_time in", values, "inviteTime");
return (Criteria) this;
}
public Criteria andInviteTimeNotIn(List<Long> values) {
addCriterion("invite_time not in", values, "inviteTime");
return (Criteria) this;
}
public Criteria andInviteTimeBetween(Long value1, Long value2) {
addCriterion("invite_time between", value1, value2, "inviteTime");
return (Criteria) this;
}
public Criteria andInviteTimeNotBetween(Long value1, Long value2) {
addCriterion("invite_time not between", value1, value2, "inviteTime");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {
protected Criteria() {
super();
}
}
public static class Criterion {
private String condition;
private Object value;
private Object secondValue;
private boolean noValue;
private boolean singleValue;
private boolean betweenValue;
private boolean listValue;
private String typeHandler;
public String getCondition() {
return condition;
}
public Object getValue() {
return value;
}
public Object getSecondValue() {
return secondValue;
}
public boolean isNoValue() {
return noValue;
}
public boolean isSingleValue() {
return singleValue;
}
public boolean isBetweenValue() {
return betweenValue;
}
public boolean isListValue() {
return listValue;
}
public String getTypeHandler() {
return typeHandler;
}
protected Criterion(String condition) {
super();
this.condition = condition;
this.typeHandler = null;
this.noValue = true;
}
protected Criterion(String condition, Object value, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.typeHandler = typeHandler;
if (value instanceof List<?>) {
this.listValue = true;
} else {
this.singleValue = true;
}
}
protected Criterion(String condition, Object value) {
this(condition, value, null);
}
protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.secondValue = secondValue;
this.typeHandler = typeHandler;
this.betweenValue = true;
}
protected Criterion(String condition, Object value, Object secondValue) {
this(condition, value, secondValue, null);
}
}
}

View File

@ -0,0 +1,40 @@
package io.metersphere.system.mapper;
import io.metersphere.system.domain.UserInvite;
import io.metersphere.system.domain.UserInviteExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface UserInviteMapper {
long countByExample(UserInviteExample example);
int deleteByExample(UserInviteExample example);
int deleteByPrimaryKey(String id);
int insert(UserInvite record);
int insertSelective(UserInvite record);
List<UserInvite> selectByExampleWithBLOBs(UserInviteExample example);
List<UserInvite> selectByExample(UserInviteExample example);
UserInvite selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") UserInvite record, @Param("example") UserInviteExample example);
int updateByExampleWithBLOBs(@Param("record") UserInvite record, @Param("example") UserInviteExample example);
int updateByExample(@Param("record") UserInvite record, @Param("example") UserInviteExample example);
int updateByPrimaryKeySelective(UserInvite record);
int updateByPrimaryKeyWithBLOBs(UserInvite record);
int updateByPrimaryKey(UserInvite record);
int batchInsert(@Param("list") List<UserInvite> list);
int batchInsertSelective(@Param("list") List<UserInvite> list, @Param("selective") UserInvite.Column ... selective);
}

View File

@ -0,0 +1,289 @@
<?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="io.metersphere.system.mapper.UserInviteMapper">
<resultMap id="BaseResultMap" type="io.metersphere.system.domain.UserInvite">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="invite_user" jdbcType="VARCHAR" property="inviteUser" />
<result column="invite_time" jdbcType="BIGINT" property="inviteTime" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.system.domain.UserInvite">
<result column="roles" jdbcType="LONGVARCHAR" property="roles" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
id, email, invite_user, invite_time
</sql>
<sql id="Blob_Column_List">
roles
</sql>
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.system.domain.UserInviteExample" resultMap="ResultMapWithBLOBs">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from user_invite
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByExample" parameterType="io.metersphere.system.domain.UserInviteExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from user_invite
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="ResultMapWithBLOBs">
select
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from user_invite
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from user_invite
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.system.domain.UserInviteExample">
delete from user_invite
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.system.domain.UserInvite">
insert into user_invite (id, email, invite_user,
invite_time, roles)
values (#{id,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{inviteUser,jdbcType=VARCHAR},
#{inviteTime,jdbcType=BIGINT}, #{roles,jdbcType=LONGVARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.system.domain.UserInvite">
insert into user_invite
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="email != null">
email,
</if>
<if test="inviteUser != null">
invite_user,
</if>
<if test="inviteTime != null">
invite_time,
</if>
<if test="roles != null">
roles,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="email != null">
#{email,jdbcType=VARCHAR},
</if>
<if test="inviteUser != null">
#{inviteUser,jdbcType=VARCHAR},
</if>
<if test="inviteTime != null">
#{inviteTime,jdbcType=BIGINT},
</if>
<if test="roles != null">
#{roles,jdbcType=LONGVARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.system.domain.UserInviteExample" resultType="java.lang.Long">
select count(*) from user_invite
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update user_invite
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR},
</if>
<if test="record.email != null">
email = #{record.email,jdbcType=VARCHAR},
</if>
<if test="record.inviteUser != null">
invite_user = #{record.inviteUser,jdbcType=VARCHAR},
</if>
<if test="record.inviteTime != null">
invite_time = #{record.inviteTime,jdbcType=BIGINT},
</if>
<if test="record.roles != null">
roles = #{record.roles,jdbcType=LONGVARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExampleWithBLOBs" parameterType="map">
update user_invite
set id = #{record.id,jdbcType=VARCHAR},
email = #{record.email,jdbcType=VARCHAR},
invite_user = #{record.inviteUser,jdbcType=VARCHAR},
invite_time = #{record.inviteTime,jdbcType=BIGINT},
roles = #{record.roles,jdbcType=LONGVARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update user_invite
set id = #{record.id,jdbcType=VARCHAR},
email = #{record.email,jdbcType=VARCHAR},
invite_user = #{record.inviteUser,jdbcType=VARCHAR},
invite_time = #{record.inviteTime,jdbcType=BIGINT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.system.domain.UserInvite">
update user_invite
<set>
<if test="email != null">
email = #{email,jdbcType=VARCHAR},
</if>
<if test="inviteUser != null">
invite_user = #{inviteUser,jdbcType=VARCHAR},
</if>
<if test="inviteTime != null">
invite_time = #{inviteTime,jdbcType=BIGINT},
</if>
<if test="roles != null">
roles = #{roles,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.system.domain.UserInvite">
update user_invite
set email = #{email,jdbcType=VARCHAR},
invite_user = #{inviteUser,jdbcType=VARCHAR},
invite_time = #{inviteTime,jdbcType=BIGINT},
roles = #{roles,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.system.domain.UserInvite">
update user_invite
set email = #{email,jdbcType=VARCHAR},
invite_user = #{inviteUser,jdbcType=VARCHAR},
invite_time = #{inviteTime,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into user_invite
(id, email, invite_user, invite_time, roles)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.email,jdbcType=VARCHAR}, #{item.inviteUser,jdbcType=VARCHAR},
#{item.inviteTime,jdbcType=BIGINT}, #{item.roles,jdbcType=LONGVARCHAR})
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
insert into user_invite (
<foreach collection="selective" item="column" separator=",">
${column.escapedColumnName}
</foreach>
)
values
<foreach collection="list" item="item" separator=",">
(
<foreach collection="selective" item="column" separator=",">
<if test="'id'.toString() == column.value">
#{item.id,jdbcType=VARCHAR}
</if>
<if test="'email'.toString() == column.value">
#{item.email,jdbcType=VARCHAR}
</if>
<if test="'invite_user'.toString() == column.value">
#{item.inviteUser,jdbcType=VARCHAR}
</if>
<if test="'invite_time'.toString() == column.value">
#{item.inviteTime,jdbcType=BIGINT}
</if>
<if test="'roles'.toString() == column.value">
#{item.roles,jdbcType=LONGVARCHAR}
</if>
</foreach>
)
</foreach>
</insert>
</mapper>

View File

@ -341,3 +341,16 @@ CREATE INDEX idx_org_id ON test_resource_pool_organization(`org_id`);
-- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT;
-- 用户邀请记录
CREATE TABLE IF NOT EXISTS user_invite
(
`id` VARCHAR(50) NOT NULL COMMENT '用户ID',
`email` VARCHAR(255) NOT NULL COMMENT '邀请邮箱',
`roles` TEXT COMMENT '所属权限',
`invite_user` VARCHAR(50) NOT NULL COMMENT '邀请用户',
`invite_time` BIGINT NOT NULL COMMENT '邀请时间',
PRIMARY KEY (id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '用户邀请记录';

View File

@ -11,16 +11,15 @@ import java.util.List;
@Data
public class TableBatchProcessDTO {
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "不处理的ID")
List<String> excludeIds;
@Schema(description = "选择的ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Valid
private List<
@NotBlank(message = "{id must not be blank}", groups = {Created.class, Updated.class})
String
> selectIds;
@Schema(description = "不处理的用户ID")
List<String> excludeIds;
@Schema(description = "是否选择所有数据")
private boolean selectAll;

View File

@ -73,8 +73,12 @@
FROM user
WHERE DELETED = 0
<if test="keyword != null and keyword != ''">
AND name LIKE CONCAT('%', #{keyword}, '%')
OR email LIKE CONCAT('%', #{keyword}, '%')
AND (
id = #{keyword} OR
(name LIKE CONCAT('%', #{keyword}, '%')
OR email LIKE CONCAT('%', #{keyword}, '%'))
)
</if>
</select>
<select id="selectUnDeletedUserIdByIdList" resultType="java.lang.String">

View File

@ -28,6 +28,9 @@ public class FilterChainUtils {
filterChainDefinitionMap.put("/sso/callback/**", "anon");
filterChainDefinitionMap.put("/license/validate", "anon");
filterChainDefinitionMap.put("/system/version/current", "anon");
//用户通过邮箱邀请自行注册的接口
filterChainDefinitionMap.put("/system/user/register-by-invite", "anon");
// for swagger
filterChainDefinitionMap.put("/swagger-ui.html", "anon");

View File

@ -88,6 +88,11 @@ user.name.not_blank=Username must not be blank
user.name.length_range=Username must be between {min} and {max} characters long
user.email.not_blank=User email must not be blank
user.email.length_range=User email must be between {min} and {max} characters long
user.email.hi=Hi
user.email.invite_ms=invited you join MeterSphere
user.email.invite_click=Join in
user.email.invite_tips=If button can not click,please click url
user.email.invite_limited_time=Url expires in 24 hours
user.email.repeat=User email already exists
user.reset.password=Reset password
user.delete=Delete user
@ -96,6 +101,9 @@ user.disable=Disable user
user.add.project=Add project
user.add.org=Add organization
user.add.group=Add group
user.invite.email=Invite by email
register.by.invite=Register by invite. Inviter is :
user.not.invite.or.expired=User not invite or expired
user.email.invalid=User email is invalid
user.status.not_blank=User status must not be blank
user.status.length_range=User status must be between {min} and {max} characters long

View File

@ -88,6 +88,11 @@ user.name.not_blank=用户名称不能为空
user.name.length_range=用户名称长度必须在{min}和{max}之间
user.email.not_blank=用户email不能为空
user.email.length_range=用户email长度必须在{min}和{max}之间
user.email.hi=你好
user.email.invite_ms=邀请你加入MeterSphere
user.email.invite_click=点击加入
user.email.invite_tips=如果按钮无法点击,请直接访问以下链接:
user.email.invite_limited_time=此链接自发送之时起24小时后过期
user.email.repeat=用户email已存在
user.reset.password=重置密码
user.delete=删除用户
@ -96,6 +101,9 @@ user.disable=禁用用户
user.add.project=添加项目
user.add.org=添加组织
user.add.group=添加用户组
user.invite.email=邮箱邀请
register.by.invite=通过邮箱邀请注册。邀请人:
user.not.invite.or.expired=该用户没有被邀请或邀请已过期
user.email.invalid=用户email格式不正确
user.status.not_blank=用户状态不能为空
user.status.length_range=用户状态长度必须在{min}和{max}之间

View File

@ -88,6 +88,11 @@ user.name.not_blank=用戶名稱不能為空
user.name.length_range=用戶名稱長度必須在{min}和{max}之間
user.email.not_blank=用戶email不能為空
user.email.length_range=用戶email長度必須在{min}和{max}之間
user.email.hi=你好
user.email.invite_ms=邀請你加入MeterSphere
user.email.invite_click=點擊加入
user.email.invite_tips=如果按鈕無法點擊,請直接訪問以下鏈接:
user.email.invite_limited_time=此鏈接自發送之時起24小時後過期
user.email.repeat=用戶email已存在
user.reset.password=重置密碼
user.delete=刪除用戶
@ -96,6 +101,9 @@ user.disable=禁用用戶
user.add.project=添加項目
user.add.org=添加組織
user.add.group=添加用戶組
user.invite.email=郵箱邀請
register.by.invite=通過郵箱邀請註冊。邀請人:
user.not.invite.or.expired=該用戶沒有被邀請或邀請已過期
user.email.invalid=用戶email格式不正確
user.status.not_blank=用戶狀態不能為空
user.status.length_range=用戶狀態長度必須在{min}和{max}之間

View File

@ -294,6 +294,18 @@ public abstract class BaseTest {
.orElseThrow(() -> new Exception("日志不存在,请补充操作日志"));
}
protected void checkLog(String resourceId, OperationLogType operationLogType, String path) throws Exception {
OperationLogExample example = new OperationLogExample();
example.createCriteria().andSourceIdEqualTo(resourceId).andTypeEqualTo(operationLogType.name()).andPathEqualTo(path);
operationLogMapper.selectByExample(example).stream()
.filter(operationLog -> operationLog.getSourceId().equalsIgnoreCase(resourceId))
.filter(operationLog -> operationLog.getType().equalsIgnoreCase(operationLogType.name()))
.filter(operationLog -> StringUtils.isNotBlank(operationLog.getProjectId()))
.filter(operationLog -> StringUtils.isNotBlank(operationLog.getModule()))
.findFirst()
.orElseThrow(() -> new Exception("[" + path + "]路径下的日志不存在,请补充操作日志"));
}
/**
* Created 分组参数校验
*/

View File

@ -14,6 +14,9 @@ import io.metersphere.sdk.util.Pager;
import io.metersphere.sdk.util.SessionUtils;
import io.metersphere.system.domain.Organization;
import io.metersphere.system.dto.UserBatchCreateDTO;
import io.metersphere.system.dto.request.UserInviteRequest;
import io.metersphere.system.dto.request.UserRegisterRequest;
import io.metersphere.system.dto.response.UserInviteResponse;
import io.metersphere.system.request.OrganizationMemberBatchRequest;
import io.metersphere.system.request.ProjectAddMemberBatchRequest;
import io.metersphere.system.request.user.UserChangeEnableRequest;
@ -176,4 +179,17 @@ public class UserController {
userLogService.batchAddOrgLog(userRoleBatchRelationRequest, SessionUtils.getUserId());
return new TableBatchProcessResponse(userRoleBatchRelationRequest.getSelectIds().size(), userRoleBatchRelationRequest.getSelectIds().size());
}
@PostMapping("/invite")
@Operation(summary = "系统设置-系统-用户-用户邀请")
@RequiresPermissions(PermissionConstants.SYSTEM_USER_ADD)
public UserInviteResponse invite(@Validated @RequestBody UserInviteRequest request) {
return userService.saveInviteRecord(request, SessionUtils.getUser());
}
@PostMapping("/register-by-invite")
@Operation(summary = "系统设置-系统-用户-用户邀请")
public String registerByInvite(@Validated @RequestBody UserRegisterRequest request) {
return userService.registerByInvite(request);
}
}

View File

@ -0,0 +1,27 @@
package io.metersphere.system.dto.request;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class UserInviteRequest {
@Schema(description = "邀请用户的Email", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "{user.email.invalid}")
List<@Valid
@NotBlank(message = "{user.email.invalid}")
@Size(min = 1, max = 64, message = "{user.email.length_range}")
@Email(message = "{user.email.invalid}")
String> inviteEmails;
@Schema(description = "邀请用户所属用户组", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "{user_role.id.not_blank}")
List<@Valid @NotBlank(message = "{user_role.id.not_blank}") String> userRoleIds;
}

View File

@ -0,0 +1,23 @@
package io.metersphere.system.dto.request;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class UserRegisterRequest {
@NotBlank
@Schema(description = "被邀请ID", requiredMode = Schema.RequiredMode.REQUIRED)
private String inviteId;
@NotBlank
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED)
private String name;
@NotBlank
@Schema(description = "用户密码", requiredMode = Schema.RequiredMode.REQUIRED)
private String password;
@Schema(description = "用户手机号")
private String phone;
}

View File

@ -0,0 +1,19 @@
package io.metersphere.system.dto.response;
import io.metersphere.system.domain.UserInvite;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
public class UserInviteResponse {
@Schema(description = "邀请记录ID")
private List<String> inviteIds;
public UserInviteResponse(List<UserInvite> userInvites) {
this.inviteIds = userInvites.stream().map(UserInvite::getId).collect(java.util.stream.Collectors.toList());
}
}

View File

@ -0,0 +1,21 @@
package io.metersphere.system.job;
import com.fit2cloud.quartz.anno.QuartzScheduled;
import io.metersphere.system.service.UserInviteService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
/**
* 定期清理用户邀请记录
*/
@Component
public class CleanUserInviteJob {
@Resource
private UserInviteService userInviteService;
@QuartzScheduled(cron = "0 0 0 * * ?")
public void cleanUserInvite() {
userInviteService.deleteByOneDayAgo();
}
}

View File

@ -0,0 +1,174 @@
package io.metersphere.system.service;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.domain.UserInvite;
import io.metersphere.system.domain.UserInviteExample;
import io.metersphere.system.mapper.UserInviteMapper;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Service
@Transactional(rollbackFor = Exception.class)
public class UserInviteService {
@Resource
private UserInviteMapper userInviteMapper;
public void deleteByOneDayAgo() {
long time = System.currentTimeMillis() - 24 * 60 * 60 * 1000;
UserInviteExample example = new UserInviteExample();
example.createCriteria().andInviteTimeLessThan(time);
userInviteMapper.deleteByExample(example);
}
public List<UserInvite> batchInsert(List<String> inviteEmails, String inviteUser, List<String> userRoleIds) {
long inviteTime = System.currentTimeMillis();
List<UserInvite> inviteList = new ArrayList<>();
for (String email : inviteEmails) {
UserInvite userInvite = new UserInvite();
userInvite.setEmail(email);
userInvite.setRoles(JSON.toJSONString(userRoleIds));
userInvite.setInviteUser(inviteUser);
userInvite.setInviteTime(inviteTime);
userInvite.setId(UUID.randomUUID().toString());
inviteList.add(userInvite);
}
userInviteMapper.batchInsert(inviteList);
return inviteList;
}
public UserInvite selectEfficientInviteById(String id) {
long time = System.currentTimeMillis() - 24 * 60 * 60 * 1000;
UserInviteExample example = new UserInviteExample();
example.createCriteria().andIdEqualTo(id).andInviteTimeGreaterThanOrEqualTo(time);
List<UserInvite> userInvites = userInviteMapper.selectByExampleWithBLOBs(example);
if (CollectionUtils.isNotEmpty(userInvites)) {
return userInvites.get(0);
} else {
return null;
}
}
public void deleteInviteById(String id) {
userInviteMapper.deleteByPrimaryKey(id);
}
public String genInviteMessage(String inviteUser, String inviteId, String baseUrl) {
String inviteUrl = baseUrl + "/#/invite?inviteId=" + inviteId;
return "<html>\n" +
" <head>\n" +
" <base target=\"_blank\">\n" +
" <style type=\"text/css\">\n" +
" ::-webkit-scrollbar{ display: none; }\n" +
" </style>\n" +
" <style id=\"cloudAttachStyle\" type=\"text/css\">\n" +
" #divNeteaseBigAttach, #divNeteaseBigAttach_bak{display:none;}\n" +
" </style>\n" +
" </head>\n" +
" <body tabindex=\"0\" role=\"listitem\">\n" +
" <div id=\"content\" class=\"netease_mail_readhtml netease_mail_readhtml_webmail\">\n" +
"\t\t\t<div lang=\"en\">\n" +
" <div style=\"font-family: Nunito, sans-serif; font-size: 14px; font-weight: 400;\">\n" +
" <section style=\"align-items: center; padding: 50px 50px;\">\n" +
" <div class=\"row align-items-center\">\n" +
" <div class=\"col-lg-3 col-md-3 \"></div>\n" +
" </div>\n" +
" <div class=\"container\">\n" +
" <div class=\"row\" style=\"justify-content: center;\">\n" +
" <div class=\"col-lg-6 col-md-8\">\n" +
" <table style=\"box-sizing: border-box; width: 100%; border-radius: 6px; overflow: hidden; background-color: #fff; box-shadow: 0 0 3px rgba(60, 72, 88, 0.15);\">\n" +
" <thead>\n" +
" <tr style=\"background-color: #783787; padding: 3px 0; line-height: 68px; text-align: center; color: #fff; font-size: 24px; font-weight: 700; letter-spacing: 1px;\">\n" +
" <th scope=\"col\"></th>\n" +
" </tr>\n" +
" </thead>\n" +
"\n" +
" <tbody>\n" +
" <tr>\n" +
" <td style=\"padding: 48px 24px 0; color: #161c2d; font-size: 18px; font-weight: 600;\">\n" +
" " + Translator.get("user.email.hi") + "\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td style=\"padding: 14px 24px 7px; color: #8492a6;\">\n" +
" <span style=\"color:#7952B3\">" + inviteUser + "</span>\n" +
" " + Translator.get("user.email.invite_ms") + "MeterSphere\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td style=\"padding: 7px 24px;\">\n" +
" <a style=\"background-color: #783787;color:#FFFFFF;padding: 4px 10px; outline:\n" +
" none;\n" +
" text-decoration: none; font-size: 16px; letter-spacing: 0.5px; transition: all 0.3s; font-weight: 600; border-radius: 6px;\" \n" +
" href=\"" + inviteUrl + "\">" + Translator.get("user.email.invite_click") + "</a>\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td style=\"padding: 7px 24px 0; color: #8492a6;\">\n" + Translator.get("user.email.invite_tips") +
" <a style=\"color:#7952B3\" href=\"" + inviteUrl + "\">" + inviteUrl + "</a>" + Translator.get("user.email.invite_limited_time") + "\n" +
" </td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td style=\"padding: 14px 24px 15px; color: #8492a6;\">\n" +
" </td>\n" +
" </tr>" +
" </tbody>\n" +
" </table>\n" +
" </div>\n" +
" </div>\n" +
" </div>\n" +
" </section>\n" +
" <style type=\"text/css\">\n" +
" table a {\n" +
" text-decoration: none;\n" +
" }\n" +
" </style>\n" +
"\n" +
" <img src=\"https://hk-callback.qcloudmail.com/api/webhook?upn=7f5fc2d81bf99757e3e56e8e756f73cb10378796923237aa92183f70851501ea89c2f4e673237df7591e377ba7c5e1ee515348617ff808a29e7164de5a1f22ddef21237de571dee4e021ccfc41101d403ab9baadb6e9155dda36f9a7b0bca966ccc18c62cdf045ba227d2fad7f4648d874a66dbadd049742bc4fda12a475073a376f24d463d3b24da6c7c8c46767f749fb0aa0e0fe4dae659597f4a4b30aeaaa26f011b4f32085716bd9f53dd245bd12af13c091726d7ebbb211d48e3a9c0137984fd4bd80d6c2ee0fe317ba75ecfe04759f252e75a998097f962ec0ea41945b8463126d092eef4a2e9132e3882cfd4e08b6e370ff07b9109e1859b43d3e8471\" alt=\"\" height=\"1\" width=\"1\" style=\"opacity:0\"></div></div>\n" +
" <div style=\"clear:both;height:1px;\"></div>\n" +
" </div>\n" +
" <script>\n" +
" var _n = document.querySelectorAll('[href], [formAction], [onclick]');\n" +
" for(var i=0;i<_n.length;i++){ \n" +
" var _nc = _n[i];\n" +
" if (_nc.getAttribute('href')) {\n" +
" _nc.setAttribute('href', _nc.getAttribute('href')), _nc.removeAttribute('href');\n" +
" }\n" +
" if (_nc.getAttribute('formAction')) {\n" +
" _nc.setAttribute('__formAction', _nc.getAttribute('formAction')), _nc.removeAttribute('formAction');\n" +
" }\n" +
" if (_nc.getAttribute('onclick')) {\n" +
" _nc.setAttribute('__onclick', _nc.getAttribute('onclick')), _nc.removeAttribute('onclick');\n" +
" }\n" +
" }\n" +
"\n" +
" </script>\n" +
" <style type=\"text/css\">\n" +
" * {\n" +
" white-space: normal !important;\n" +
" word-break: break-word !important;\n" +
" }\n" +
" body{font-size:14px;font-family:arial,verdana,sans-serif;line-height:1.666;padding:0;margin:0;overflow:auto;white-space:normal;word-wrap:break-word;min-height:100px}\n" +
" td, input, button, select, body{font-family:Helvetica, 'Microsoft Yahei', verdana}\n" +
" pre {white-space:pre-wrap !important;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;width:95%}\n" +
" pre * { white-space: unset !important; }\n" +
" th,td{font-family:arial,verdana,sans-serif;line-height:1.666}\n" +
" img{ border:0}\n" +
" header,footer,section,aside,article,nav,hgroup,figure,figcaption{display:block}\n" +
" blockquote{margin-right:0px}\n" +
" </style>\n" +
" <style id=\"ntes_link_color\" type=\"text/css\">a,td a{color:#3370FF}</style>\n" +
" </body>\n" +
"</html>";
// user.email.not_blank
}
}

View File

@ -141,7 +141,7 @@ public class UserLogService {
LogDTO dto = LogDTOBuilder.builder()
.projectId(OperationLogConstants.SYSTEM)
.organizationId(OperationLogConstants.SYSTEM)
.type(OperationLogType.UPDATE.name())
.type(OperationLogType.DELETE.name())
.module(OperationLogModule.SETTING_SYSTEM_USER_SINGLE)
.method(HttpMethodConstants.POST.name())
.path("/delete")
@ -205,7 +205,7 @@ public class UserLogService {
.sourceId(user.getId())
.type(OperationLogType.UPDATE.name())
.content(user.getName() + Translator.get("user.add.group") + ":" + roleNames)
.path("/system/user/add-org-member")
.path("/system/user/add/batch/user-role")
.method(HttpMethodConstants.POST.name())
.modifiedValue(JSON.toJSONBytes(request.getRoleIds()))
.build().getLogDTO();
@ -243,4 +243,41 @@ public class UserLogService {
operationLogService.batchAdd(logs);
}
public void addEmailInviteLog(List<UserInvite> userInviteList, String inviteUserId) {
User inviteUser = userMapper.selectByPrimaryKey(inviteUserId);
List<LogDTO> saveLogs = new ArrayList<>();
userInviteList.forEach(userInvite -> {
LogDTO log = LogDTOBuilder.builder()
.projectId(OperationLogConstants.SYSTEM)
.module(OperationLogModule.SETTING_SYSTEM_USER_SINGLE)
.createUser(inviteUserId)
.organizationId(OperationLogConstants.SYSTEM)
.sourceId(inviteUserId)
.type(OperationLogType.ADD.name())
.content(inviteUser.getName() + Translator.get("user.invite.email") + ":" + userInvite.getEmail())
.path("/system/user/invite")
.method(HttpMethodConstants.POST.name())
.modifiedValue(JSON.toJSONBytes(userInvite))
.build().getLogDTO();
saveLogs.add(log);
});
operationLogService.batchAdd(saveLogs);
}
public void addRegisterLog(User user, UserInvite userInvite) {
User inviteUser = userMapper.selectByPrimaryKey(userInvite.getInviteUser());
LogDTO log = LogDTOBuilder.builder()
.projectId(OperationLogConstants.SYSTEM)
.module(OperationLogModule.SETTING_SYSTEM_USER_SINGLE)
.createUser(user.getName())
.organizationId(OperationLogConstants.SYSTEM)
.sourceId(user.getId())
.type(OperationLogType.ADD.name())
.content(user.getName() + Translator.get("register.by.invite") + inviteUser.getName())
.path("/system/user/register-by-invite")
.method(HttpMethodConstants.POST.name())
.modifiedValue(JSON.toJSONBytes(userInvite))
.build().getLogDTO();
operationLogService.add(log);
}
}

View File

@ -107,6 +107,24 @@ public class UserRoleRelationService {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
public void batchSave(List<String> userRoleIdList, User user) {
long operationTime = System.currentTimeMillis();
List<UserRoleRelation> userRoleRelationSaveList = new ArrayList<>();
//添加用户组织关系
for (String userRoleId : userRoleIdList) {
UserRoleRelation userRoleRelation = new UserRoleRelation();
userRoleRelation.setId(UUID.randomUUID().toString());
userRoleRelation.setUserId(user.getId());
userRoleRelation.setRoleId(userRoleId);
userRoleRelation.setSourceId(UserRoleScope.SYSTEM);
userRoleRelation.setOrganizationId(UserRoleScope.SYSTEM);
userRoleRelation.setCreateTime(operationTime);
userRoleRelation.setCreateUser(user.getCreateUser());
userRoleRelationSaveList.add(userRoleRelation);
}
userRoleRelationMapper.batchInsert(userRoleRelationSaveList);
}
public Map<String, UserTableResponse> selectGlobalUserRoleAndOrganization(@Valid @NotEmpty List<String> userIdList) {
List<UserRoleRelation> userRoleRelationList = extUserRoleRelationMapper.selectGlobalRoleByUserIdList(userIdList);
List<String> userRoleIdList = userRoleRelationList.stream().map(UserRoleRelation::getRoleId).distinct().collect(Collectors.toList());

View File

@ -1,22 +1,25 @@
package io.metersphere.system.service;
import com.alibaba.excel.EasyExcelFactory;
import io.metersphere.sdk.constants.ParamConstants;
import io.metersphere.sdk.constants.UserSource;
import io.metersphere.sdk.dto.*;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.log.service.OperationLogService;
import io.metersphere.sdk.mapper.BaseUserMapper;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.CodingUtil;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.domain.User;
import io.metersphere.system.domain.UserExample;
import io.metersphere.sdk.notice.sender.impl.MailNoticeSender;
import io.metersphere.sdk.util.*;
import io.metersphere.system.domain.*;
import io.metersphere.system.dto.UserBatchCreateDTO;
import io.metersphere.system.dto.UserCreateInfo;
import io.metersphere.system.dto.UserExtend;
import io.metersphere.system.dto.excel.UserExcel;
import io.metersphere.system.dto.excel.UserExcelRowDTO;
import io.metersphere.system.dto.request.UserInviteRequest;
import io.metersphere.system.dto.request.UserRegisterRequest;
import io.metersphere.system.dto.response.UserInviteResponse;
import io.metersphere.system.mapper.ExtUserMapper;
import io.metersphere.system.mapper.SystemParameterMapper;
import io.metersphere.system.mapper.UserMapper;
import io.metersphere.system.request.user.UserChangeEnableRequest;
import io.metersphere.system.request.user.UserEditRequest;
@ -24,6 +27,8 @@ import io.metersphere.system.response.user.UserImportResponse;
import io.metersphere.system.response.user.UserTableResponse;
import io.metersphere.system.utils.UserImportEventListener;
import jakarta.annotation.Resource;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
@ -34,14 +39,13 @@ import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
@Service
@ -54,6 +58,8 @@ public class UserService {
@Resource
private ExtUserMapper extUserMapper;
@Resource
private UserInviteService userInviteService;
@Resource
private UserRoleRelationService userRoleRelationService;
@Resource
private OperationLogService operationLogService;
@ -63,28 +69,26 @@ public class UserService {
private UserRoleService userRoleService;
@Resource
private SqlSessionFactory sqlSessionFactory;
@Resource
private UserLogService userLogService;
@Resource
private UserToolService userToolService;
private void validateUserInfo(List<UserCreateInfo> userList) {
private void validateUserInfo(List<String> createEmails) {
//判断参数内是否含有重复邮箱
List<String> emailList = new ArrayList<>();
List<String> repeatEmailList = new ArrayList<>();
var userInDbMap = baseUserMapper.selectUserIdByEmailList(
userList.stream().map(UserCreateInfo::getEmail).collect(Collectors.toList()))
var userInDbMap = baseUserMapper.selectUserIdByEmailList(createEmails)
.stream().collect(Collectors.toMap(User::getEmail, User::getId));
for (UserCreateInfo user : userList) {
if (emailList.contains(user.getEmail())) {
repeatEmailList.add(user.getEmail());
for (String createEmail : createEmails) {
if (emailList.contains(createEmail)) {
repeatEmailList.add(createEmail);
} else {
//判断邮箱是否已存在数据库中
if (userInDbMap.containsKey(user.getEmail())) {
repeatEmailList.add(user.getEmail());
if (userInDbMap.containsKey(createEmail)) {
repeatEmailList.add(createEmail);
} else {
emailList.add(user.getEmail());
emailList.add(createEmail);
}
}
}
@ -94,7 +98,9 @@ public class UserService {
}
public UserBatchCreateDTO addUser(UserBatchCreateDTO userCreateDTO, String source, String operator) {
this.validateUserInfo(userCreateDTO.getUserInfoList());
//检查用户邮箱的合法性
this.validateUserInfo(userCreateDTO.getUserInfoList().stream().map(UserCreateInfo::getEmail).collect(Collectors.toList()));
//检查用户权限的合法性
globalUserRoleService.checkRoleIsGlobalAndHaveMember(userCreateDTO.getUserRoleIdList(), true);
return this.saveUserAndRole(userCreateDTO, source, operator);
}
@ -360,4 +366,111 @@ public class UserService {
public List<User> getUserListByOrgId(String organizationId, String keyword) {
return extUserMapper.getUserListByOrgId(organizationId, keyword);
}
/**
* 临时发送Email的方法
*
* @param hashMap
*/
@Resource
MailNoticeSender mailNoticeSender;
@Resource
private SystemParameterMapper systemParameterMapper;
public UserInviteResponse saveInviteRecord(UserInviteRequest request, SessionUser inviteUser) {
//校验邮箱和角色的合法性
this.validateUserInfo(request.getInviteEmails());
globalUserRoleService.checkRoleIsGlobalAndHaveMember(request.getUserRoleIds(), true);
List<UserInvite> inviteList = userInviteService.batchInsert(request.getInviteEmails(), inviteUser.getId(), request.getUserRoleIds());
//记录日志
userLogService.addEmailInviteLog(inviteList, inviteUser.getId());
this.sendInviteEmail(inviteList, inviteUser.getName());
return new UserInviteResponse(inviteList);
}
private void sendInviteEmail(List<UserInvite> inviteList, String inviteUser) {
HashMap<String, String> emailMap = new HashMap<>();
List<SystemParameter> systemParameters = systemParameterMapper.selectByExample(new SystemParameterExample());
systemParameters.forEach(systemParameter -> {
if (systemParameter.getParamKey().equals(ParamConstants.MAIL.PASSWORD.getValue())) {
if (!StringUtils.isBlank(systemParameter.getParamValue())) {
String string = EncryptUtils.aesDecrypt(systemParameter.getParamValue()).toString();
emailMap.put(systemParameter.getParamKey(), string);
}
} else {
emailMap.put(systemParameter.getParamKey(), systemParameter.getParamValue());
}
});
inviteList.forEach(userInvite -> {
String emailContent = userInviteService.genInviteMessage(inviteUser, userInvite.getId(), emailMap.get("base.url"));
emailMap.put("emailContent", emailContent);
emailMap.put("smtp.recipient", userInvite.getEmail());
try {
this.sendInviteEmailTemporary(emailMap);
} catch (Exception e) {
LogUtils.error("邮箱邀请失败!", e);
}
});
}
public void sendInviteEmailTemporary(HashMap<String, String> hashMap) throws Exception {
//todo 发送邮件 等小美女的消息通知提交完毕之后删除
JavaMailSenderImpl javaMailSender = null;
try {
javaMailSender = mailNoticeSender.getMailSender(hashMap);
javaMailSender.testConnection();
} catch (Exception e) {
LogUtils.error(e.getMessage(), e);
throw new MSException(Translator.get("connection_failed"));
}
String recipients = hashMap.get(ParamConstants.MAIL.RECIPIENTS.getValue());
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
String username = javaMailSender.getUsername();
String email = username;
InternetAddress from = new InternetAddress();
from.setAddress(email);
from.setPersonal(username);
helper.setFrom(from);
helper.setSubject("MeterSphere邀请注册");
helper.setText(hashMap.get("emailContent"), true);
helper.setTo(recipients);
javaMailSender.send(mimeMessage);
}
public String registerByInvite(UserRegisterRequest request) {
UserInvite userInvite = userInviteService.selectEfficientInviteById(request.getInviteId());
if (userInvite == null) {
throw new MSException(Translator.get("user.not.invite.or.expired"));
}
//检查邮箱是否已经注册
this.validateUserInfo(new ArrayList<>() {{
this.add(userInvite.getEmail());
}});
//创建用户
long createTime = System.currentTimeMillis();
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setEmail(userInvite.getEmail());
user.setPassword(request.getPassword());
user.setName(request.getName());
user.setPhone(request.getPhone());
user.setCreateUser(userInvite.getInviteUser());
user.setUpdateUser(userInvite.getInviteUser());
user.setCreateTime(createTime);
user.setUpdateTime(createTime);
user.setSource(UserSource.LOCAL.name());
user.setDeleted(false);
userMapper.insertSelective(user);
userRoleRelationService.batchSave(JSON.parseArray(userInvite.getRoles(), String.class), user);
//删除本次邀请记录
userInviteService.deleteInviteById(userInvite.getId());
//写入操作日志
userLogService.addRegisterLog(user, userInvite);
return user.getId();
}
}

View File

@ -69,29 +69,30 @@
</javaClientGenerator>
<!--要生成的数据库表 -->
<table tableName="auth_source"/>
<table tableName="license"/>
<table tableName="message_task"/>
<table tableName="message_task_blob"/>
<table tableName="notification"/>
<table tableName="novice_statistics"/>
<table tableName="operating_log"/>
<table tableName="operating_log_resource"/>
<table tableName="plugin"/>
<table tableName="plugin_script"/>
<table tableName="plugin_organization"/>
<table tableName="schedule"/>
<table tableName="service_integration"/>
<table tableName="system_parameter"/>
<table tableName="test_resource"/>
<table tableName="test_resource_pool"/>
<table tableName="user"/>
<table tableName="user_extend"/>
<table tableName="user_key"/>
<table tableName="user_role"/>
<table tableName="user_role_permission"/>
<table tableName="user_role_relation"/>
<table tableName="organization"/>
<table tableName="user_invite"/>
<!-- <table tableName="auth_source"/>-->
<!-- <table tableName="license"/>-->
<!-- <table tableName="message_task"/>-->
<!-- <table tableName="message_task_blob"/>-->
<!-- <table tableName="notification"/>-->
<!-- <table tableName="novice_statistics"/>-->
<!-- <table tableName="operating_log"/>-->
<!-- <table tableName="operating_log_resource"/>-->
<!-- <table tableName="plugin"/>-->
<!-- <table tableName="plugin_script"/>-->
<!-- <table tableName="plugin_organization"/>-->
<!-- <table tableName="schedule"/>-->
<!-- <table tableName="service_integration"/>-->
<!-- <table tableName="system_parameter"/>-->
<!-- <table tableName="test_resource"/>-->
<!-- <table tableName="test_resource_pool"/>-->
<!-- <table tableName="user"/>-->
<!-- <table tableName="user_extend"/>-->
<!-- <table tableName="user_key"/>-->
<!-- <table tableName="user_role"/>-->
<!-- <table tableName="user_role_permission"/>-->
<!-- <table tableName="user_role_relation"/>-->
<!-- <table tableName="organization"/>-->
<!-- 要忽略的字段-->
<!-- <table tableName="test_case">

View File

@ -4,6 +4,7 @@ import io.metersphere.sdk.base.BaseTest;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.dto.TableBatchProcessDTO;
import io.metersphere.system.dto.UserCreateInfo;
import io.metersphere.system.dto.request.UserInviteRequest;
import io.metersphere.system.request.user.UserChangeEnableRequest;
import io.metersphere.system.request.user.UserRoleBatchRelationRequest;
import io.metersphere.system.response.user.UserSelectOption;
@ -111,6 +112,12 @@ public class UserControllerPermissionTests extends BaseTest {
this.requestPostPermissionsTest(addMemberPermissionList, UserRequestUtils.URL_ADD_PROJECT_MEMBER, roleBatchRelationRequest);
// 批量添加用户到组织
this.requestPostPermissionsTest(addMemberPermissionList, UserRequestUtils.URL_ADD_ORGANIZATION_MEMBER, roleBatchRelationRequest);
// 邀请用户
UserInviteRequest userInviteRequest = new UserInviteRequest();
userInviteRequest.setUserRoleIds(Collections.singletonList("member"));
userInviteRequest.setInviteEmails(Collections.singletonList("tianyang.song.invite.permission.1@test.email"));
this.requestPostPermissionTest(PermissionConstants.SYSTEM_USER_ADD, UserRequestUtils.URL_INVITE, userInviteRequest);
}
}

View File

@ -12,10 +12,15 @@ import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.Pager;
import io.metersphere.system.domain.User;
import io.metersphere.system.domain.UserExample;
import io.metersphere.system.domain.UserInvite;
import io.metersphere.system.domain.UserRoleRelationExample;
import io.metersphere.system.dto.UserBatchCreateDTO;
import io.metersphere.system.dto.UserCreateInfo;
import io.metersphere.system.dto.excel.UserExcelRowDTO;
import io.metersphere.system.dto.request.UserInviteRequest;
import io.metersphere.system.dto.request.UserRegisterRequest;
import io.metersphere.system.dto.response.UserInviteResponse;
import io.metersphere.system.mapper.UserInviteMapper;
import io.metersphere.system.mapper.UserMapper;
import io.metersphere.system.mapper.UserRoleRelationMapper;
import io.metersphere.system.request.user.UserChangeEnableRequest;
@ -66,6 +71,8 @@ public class UserControllerTests extends BaseTest {
ProjectMapper projectMapper;
@Resource
private UserRoleRelationMapper userRoleRelationMapper;
//邀请记录
private static final List<String> INVITE_RECORD_ID_LIST = new ArrayList<>();
//失败请求返回编码
private static final ResultMatcher BAD_REQUEST_MATCHER = status().isBadRequest();
@ -81,6 +88,8 @@ public class UserControllerTests extends BaseTest {
public static final String USER_NONE_ROLE_EMAIL = "tianyang.none.role@163.com";
//已删除的用户ID
private static final List<String> DELETED_USER_ID_LIST = new ArrayList<>();
@Resource
private UserInviteMapper userInviteMapper;
UserRequestUtils userRequestUtils = null;
@ -662,7 +671,7 @@ public class UserControllerTests extends BaseTest {
UserExample userExample = new UserExample();
userExample.createCriteria().andIdEqualTo("admin").andPasswordEqualTo(CodingUtil.md5("metersphere"));
Assertions.assertEquals(1, userMapper.countByExample(userExample));
this.checkLog("admin", OperationLogType.UPDATE);
this.checkLog("admin", OperationLogType.UPDATE, UserRequestUtils.URL_USER_RESET_PASSWORD);
}
//重置普通用户密码
{
@ -683,7 +692,7 @@ public class UserControllerTests extends BaseTest {
UserExample userExample = new UserExample();
userExample.createCriteria().andIdEqualTo(checkUser.getId()).andPasswordEqualTo(CodingUtil.md5(checkUser.getEmail()));
Assertions.assertEquals(1, userMapper.countByExample(userExample));
this.checkLog(checkUser.getId(), OperationLogType.UPDATE);
this.checkLog(checkUser.getId(), OperationLogType.UPDATE, UserRequestUtils.URL_USER_RESET_PASSWORD);
}
}
//重置非Admin用户的密码
@ -707,7 +716,7 @@ public class UserControllerTests extends BaseTest {
UserExample userExample = new UserExample();
userExample.createCriteria().andIdEqualTo(checkUser.getId()).andPasswordEqualTo(CodingUtil.md5(checkUser.getEmail()));
Assertions.assertEquals(1, userMapper.countByExample(userExample));
this.checkLog(checkUser.getId(), OperationLogType.UPDATE);
this.checkLog(checkUser.getId(), OperationLogType.UPDATE, UserRequestUtils.URL_USER_RESET_PASSWORD);
}
}
}
@ -732,7 +741,7 @@ public class UserControllerTests extends BaseTest {
);
//检查日志
for (String userID : request.getSelectIds()) {
this.checkLog(userID, OperationLogType.ADD);
this.checkLog(userID, OperationLogType.UPDATE, UserRequestUtils.URL_USER_ROLE_RELATION);
}
//测试重复添加用户权限预期结果不会额外增加数据
@ -866,7 +875,7 @@ public class UserControllerTests extends BaseTest {
}
//检查日志
for (String userID : request.getSelectIds()) {
this.checkLog(userID, OperationLogType.UPDATE);
this.checkLog(userID, OperationLogType.UPDATE, UserRequestUtils.URL_ADD_PROJECT_MEMBER);
}
//检查用户表格不会加载出来非全局用户组
this.testPageSuccess();
@ -903,7 +912,7 @@ public class UserControllerTests extends BaseTest {
}
//检查日志
for (String userID : request.getSelectIds()) {
this.checkLog(userID, OperationLogType.UPDATE);
this.checkLog(userID, OperationLogType.UPDATE, UserRequestUtils.URL_ADD_ORGANIZATION_MEMBER);
}
//检查用户表格加载组织
this.testPageSuccess();
@ -959,13 +968,33 @@ public class UserControllerTests extends BaseTest {
userRequestUtils.requestPost(UserRequestUtils.URL_ADD_PROJECT_MEMBER, orgRequest, ERROR_REQUEST_MATCHER);
}
@Test
@Order(12)
public void testUserInvite() throws Exception {
if (CollectionUtils.isEmpty(USER_LIST)) {
this.testAddSuccess();
}
this.testUserInviteSuccess();
this.testUserInviteError();
}
@Test
@Order(13)
public void testUserRegister() throws Exception {
if (CollectionUtils.isEmpty(INVITE_RECORD_ID_LIST)) {
this.testUserInvite();
}
this.testUserRegisterSuccess();
this.testUserRegisterError();
}
//本测试类中会用到很多次用户数据所以测试删除的方法放于最后
@Test
@Order(99)
public void testUserDeleteSuccess() throws Exception {
this.checkUserList();
//删除USER_LIST用户
TableBatchProcessDTO request = new TableBatchProcessDTO();
TableBatchProcessDTO request = new TableBatchProcessDTO();
request.setSelectIds(USER_LIST.stream().map(UserCreateInfo::getId).toList());
TableBatchProcessResponse response = userRequestUtils.parseObjectFromMvcResult(
userRequestUtils.responsePost(UserRequestUtils.URL_USER_DELETE, request), TableBatchProcessResponse.class);
@ -976,6 +1005,8 @@ public class UserControllerTests extends BaseTest {
for (UserCreateInfo deleteUser : USER_LIST) {
User user = userMapper.selectByPrimaryKey(deleteUser.getId());
Assertions.assertTrue(user.getDeleted());
//检查日志
this.checkLog(deleteUser.getId(), OperationLogType.DELETE, UserRequestUtils.URL_USER_DELETE);
removeList.add(deleteUser);
}
USER_LIST.removeAll(removeList);
@ -1054,4 +1085,167 @@ public class UserControllerTests extends BaseTest {
}
return returnList;
}
public void testUserInviteSuccess() throws Exception {
UserInviteRequest userInviteRequest = UserParamUtils.getUserInviteRequest(
USER_ROLE_LIST,
new ArrayList<>() {{
add("tianyang.song.invite.1@test.email");
add("tianyang.song.invite.2@test.email");
}}
);
MvcResult mvcResult = userRequestUtils.responsePost(UserRequestUtils.URL_INVITE, userInviteRequest);
String resultHolderStr = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(resultHolderStr, ResultHolder.class);
UserInviteResponse response = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), UserInviteResponse.class);
Assertions.assertEquals(2, response.getInviteIds().size());
//检查日志 此处日志的资源是邀请的用户即admin
this.checkLog("admin", OperationLogType.ADD, UserRequestUtils.URL_INVITE);
INVITE_RECORD_ID_LIST.addAll(response.getInviteIds());
}
public void testUserInviteError() throws Exception {
List<String> inviteEmailList = new ArrayList<>() {{
add("tianyang.song.invite.error.1@test.email");
add("tianyang.song.invite.error.2@test.email");
}};
//400-用户角色为空
UserInviteRequest userInviteRequest = UserParamUtils.getUserInviteRequest(
new ArrayList<>(),
inviteEmailList
);
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE, userInviteRequest, BAD_REQUEST_MATCHER);
userInviteRequest.setUserRoleIds(null);
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE, userInviteRequest, BAD_REQUEST_MATCHER);
//400-邀请用户为空
userInviteRequest = UserParamUtils.getUserInviteRequest(
USER_ROLE_LIST,
new ArrayList<>()
);
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE, userInviteRequest, BAD_REQUEST_MATCHER);
userInviteRequest.setInviteEmails(null);
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE, userInviteRequest, BAD_REQUEST_MATCHER);
//400-邀请邮箱又非正确的格式的
userInviteRequest = UserParamUtils.getUserInviteRequest(
USER_ROLE_LIST,
new ArrayList<>() {{
this.addAll(inviteEmailList);
this.add("tianyang.song.invite.error.3");
}}
);
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE, userInviteRequest, BAD_REQUEST_MATCHER);
//500-包含无效权限
userInviteRequest = UserParamUtils.getUserInviteRequest(
USER_ROLE_LIST,
inviteEmailList
);
userInviteRequest.getUserRoleIds().add("none role");
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE, userInviteRequest, ERROR_REQUEST_MATCHER);
//500-用户邮箱数据内重复
userInviteRequest = UserParamUtils.getUserInviteRequest(
USER_ROLE_LIST,
inviteEmailList
);
userInviteRequest.getInviteEmails().addAll(inviteEmailList);
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE, userInviteRequest, ERROR_REQUEST_MATCHER);
//500-用户邮箱在数据库中已存在
userInviteRequest = UserParamUtils.getUserInviteRequest(
USER_ROLE_LIST,
inviteEmailList
);
userInviteRequest.getInviteEmails().add(USER_LIST.get(0).getEmail());
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE, userInviteRequest, ERROR_REQUEST_MATCHER);
}
private void testUserRegisterSuccess() throws Exception {
String inviteId = INVITE_RECORD_ID_LIST.get(0);
UserRegisterRequest request = new UserRegisterRequest();
request.setInviteId(inviteId);
request.setName("建国通过邮箱邀请");
request.setPassword(UUID.randomUUID().toString());
MvcResult mvcResult = userRequestUtils.responsePost(UserRequestUtils.URL_INVITE_REGISTER, request);
String resultHolderStr = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(resultHolderStr, ResultHolder.class);
//检查日志 此处日志的资源是邀请的用户即admin
this.checkLog(resultHolder.getData().toString(), OperationLogType.ADD, UserRequestUtils.URL_INVITE_REGISTER);
}
private void testUserRegisterError() throws Exception {
if (INVITE_RECORD_ID_LIST.isEmpty()) {
this.testUserInviteSuccess();
}
String inviteId = INVITE_RECORD_ID_LIST.get(1);
//400-用户名为空
UserRegisterRequest request = new UserRegisterRequest();
request.setInviteId(inviteId);
request.setPassword(UUID.randomUUID().toString());
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE_REGISTER, request, BAD_REQUEST_MATCHER);
request.setName("");
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE_REGISTER, request, BAD_REQUEST_MATCHER);
//400-用户密码为空
request = new UserRegisterRequest();
request.setInviteId(inviteId);
request.setName("建国通过邮箱邀请2");
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE_REGISTER, request, BAD_REQUEST_MATCHER);
request.setPassword("");
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE_REGISTER, request, BAD_REQUEST_MATCHER);
//400-邀请ID为空
request = new UserRegisterRequest();
request.setName("建国通过邮箱邀请2");
request.setPassword(UUID.randomUUID().toString());
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE_REGISTER, request, BAD_REQUEST_MATCHER);
request.setInviteId("");
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE_REGISTER, request, BAD_REQUEST_MATCHER);
//500-邀请ID不存在
request = new UserRegisterRequest();
request.setInviteId(UUID.randomUUID().toString());
request.setName("建国通过邮箱邀请2");
request.setPassword(UUID.randomUUID().toString());
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE_REGISTER, request, ERROR_REQUEST_MATCHER);
//500-邀请ID已过期且暂未删除
UserInvite invite = userInviteMapper.selectByPrimaryKey(inviteId);
invite.setInviteTime(invite.getInviteTime() - 1000 * 60 * 60 * 24);
userInviteMapper.updateByPrimaryKeySelective(invite);
request = new UserRegisterRequest();
request.setInviteId(inviteId);
request.setName("建国通过邮箱邀请2");
request.setPassword(UUID.randomUUID().toString());
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE_REGISTER, request, ERROR_REQUEST_MATCHER);
//500-用户邮箱在用户注册之前已经被注册过了
//首先还原邀请时间
invite = userInviteMapper.selectByPrimaryKey(inviteId);
invite.setInviteTime(System.currentTimeMillis());
userInviteMapper.updateByPrimaryKeySelective(invite);
String insertEmail = invite.getEmail();
UserBatchCreateDTO userMaintainRequest = UserParamUtils.getUserCreateDTO(
USER_ROLE_LIST,
new ArrayList<>() {{
add(new UserCreateInfo() {{
setName(insertEmail);
setEmail(insertEmail);
}});
}}
);
userRequestUtils.responsePost(UserRequestUtils.URL_USER_CREATE, userMaintainRequest);
//测试
request = new UserRegisterRequest();
request.setInviteId(inviteId);
request.setName("建国通过邮箱邀请2");
request.setPassword(UUID.randomUUID().toString());
userRequestUtils.requestPost(UserRequestUtils.URL_INVITE_REGISTER, request, ERROR_REQUEST_MATCHER);
}
}

View File

@ -0,0 +1,21 @@
package io.metersphere.system.job;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CleanUserInviteJobTests {
@Resource
private CleanUserInviteJob cleanUserInviteJob;
@Test
public void cleanupProject() {
cleanUserInviteJob.cleanUserInvite();
}
}

View File

@ -4,15 +4,14 @@ import io.metersphere.sdk.constants.UserRoleScope;
import io.metersphere.sdk.constants.UserRoleType;
import io.metersphere.sdk.dto.BasePageRequest;
import io.metersphere.sdk.dto.UserDTO;
import io.metersphere.sdk.service.BaseUserRoleService;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.system.domain.UserRole;
import io.metersphere.system.dto.UserBatchCreateDTO;
import io.metersphere.system.dto.UserCreateInfo;
import io.metersphere.system.dto.request.UserInviteRequest;
import io.metersphere.system.request.user.UserEditRequest;
import io.metersphere.system.response.user.UserImportResponse;
import io.metersphere.system.response.user.UserSelectOption;
import io.metersphere.system.service.GlobalUserRoleService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Assertions;
@ -36,6 +35,18 @@ public class UserParamUtils {
return userMaintainRequest;
}
public static UserInviteRequest getUserInviteRequest(
List<UserSelectOption> userRoleList,
List<String> inviteEmailList) {
UserInviteRequest request = new UserInviteRequest();
if (CollectionUtils.isNotEmpty(userRoleList)) {
request.setUserRoleIds(
userRoleList.stream().map(UserSelectOption::getId).collect(Collectors.toList()));
}
request.setInviteEmails(inviteEmailList);
return request;
}
public static UserEditRequest getUserUpdateDTO(UserCreateInfo user, List<UserSelectOption> userRoleList) {
UserEditRequest returnDTO = new UserEditRequest();
if (user.getPhone() == null) {

View File

@ -39,8 +39,13 @@ public class UserRequestUtils {
public static final String URL_ADD_PROJECT_MEMBER = "/system/user/add-project-member";
public static final String URL_ADD_ORGANIZATION_MEMBER = "/system/user/add-org-member";
//用户邀请
public static final String URL_INVITE = "/system/user/invite";
//用户邀请
public static final String URL_INVITE_REGISTER = "/system/user/register-by-invite";
private final MockMvc mockMvc;
private final String sessionId;
public final String sessionId;
private final String csrfToken;
public UserRequestUtils(MockMvc mockMvc, String sessionId, String csrfToken) {
@ -49,6 +54,7 @@ public class UserRequestUtils {
this.csrfToken = csrfToken;
}
//解析返回值
public <T> T parseObjectFromMvcResult(MvcResult mvcResult, Class<T> parseClass) {
String returnData = "";