feat(client): 实现账号设备试用期管理功能

- 新增设备试用期过期时间字段及管理接口
- 实现试用期状态检查与过期提醒逻辑
- 支持账号类型区分试用与付费用户
- 添加设备注册时自动设置3天试用期- 实现VIP状态刷新与过期类型判断
-优化账号列表查询支持按客户端用户名过滤
- 更新客户端设备管理支持试用期控制- 完善登录流程支持试用期状态提示
-修复设备离线通知缺少用户名参数问题
- 调整账号默认设置清除逻辑关联客户端用户名
This commit is contained in:
2025-10-17 14:17:02 +08:00
parent 132299c4b7
commit 6e1b4d00de
18 changed files with 348 additions and 129 deletions

View File

@@ -17,6 +17,8 @@ public class BanmaAccount extends BaseEntity {
private String username;
/** 登录密码服务端刷新Token用不对外返回 */
private String password;
/** 客户端账号用户名关联client_account.username */
private String clientUsername;
/** 访问 Token客户端刷新后回写 */
private String token;
/** Token 过期时间(可选) */
@@ -37,6 +39,8 @@ public class BanmaAccount extends BaseEntity {
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getClientUsername() { return clientUsername; }
public void setClientUsername(String clientUsername) { this.clientUsername = clientUsername; }
public String getToken() { return token; }
public void setToken(String token) { this.token = token; }
public Date getTokenExpireAt() { return tokenExpireAt; }

View File

@@ -51,6 +51,10 @@ public class ClientAccount extends BaseEntity
@Excel(name = "设备数量限制")
private Integer deviceLimit;
/** 账号类型 */
@Excel(name = "账号类型")
private String accountType; // trial试用, paid付费
public void setId(Long id)
{
this.id = id;
@@ -147,4 +151,14 @@ public class ClientAccount extends BaseEntity
{
return deviceLimit;
}
public void setAccountType(String accountType)
{
this.accountType = accountType;
}
public String getAccountType()
{
return accountType;
}
}

View File

@@ -30,6 +30,9 @@ public class ClientDevice extends BaseEntity {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "最近在线", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date lastActiveAt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "试用期过期时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date trialExpireTime;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@@ -49,6 +52,8 @@ public class ClientDevice extends BaseEntity {
public void setLocation(String location) { this.location = location; }
public Date getLastActiveAt() { return lastActiveAt; }
public void setLastActiveAt(Date lastActiveAt) { this.lastActiveAt = lastActiveAt; }
public Date getTrialExpireTime() { return trialExpireTime; }
public void setTrialExpireTime(Date trialExpireTime) { this.trialExpireTime = trialExpireTime; }
}

View File

@@ -1,6 +1,7 @@
package com.ruoyi.system.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.system.domain.BanmaAccount;
/**
@@ -12,7 +13,7 @@ public interface BanmaAccountMapper {
int insert(BanmaAccount entity);
int update(BanmaAccount entity);
int deleteById(Long id);
int clearDefault();
int clearDefault(@Param("clientUsername") String clientUsername);
}

View File

@@ -1,18 +1,20 @@
package com.ruoyi.system.mapper;
import com.ruoyi.system.domain.ClientDevice;
import io.lettuce.core.dynamic.annotation.Param;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ClientDeviceMapper {
ClientDevice selectByDeviceId(String deviceId);
ClientDevice selectByDeviceId(@Param("deviceId") String deviceId);
ClientDevice selectByDeviceIdAndUsername(@Param("deviceId") String deviceId, @Param("username") String username);
List<ClientDevice> selectByUsername(@Param("username") String username);
List<ClientDevice> selectOnlineDevices();
int insert(ClientDevice device);
int updateByDeviceId(ClientDevice device);
int deleteByDeviceId(String deviceId);
int countByUsername(String username);
int updateByDeviceIdAndUsername(ClientDevice device);
int deleteByDeviceId(@Param("deviceId") String deviceId);
int countByUsername(@Param("username") String username);
}

View File

@@ -26,7 +26,14 @@ public class BanmaAccountServiceImpl implements IBanmaAccountService {
@Override
public List<BanmaAccount> listSimple() {
List<BanmaAccount> list = mapper.selectList(new BanmaAccount());
return listSimple(null);
}
@Override
public List<BanmaAccount> listSimple(String clientUsername) {
BanmaAccount query = new BanmaAccount();
query.setClientUsername(clientUsername);
List<BanmaAccount> list = mapper.selectList(query);
// 隐藏密码
for (BanmaAccount a : list) { a.setPassword(null); }
return list;
@@ -34,13 +41,25 @@ public class BanmaAccountServiceImpl implements IBanmaAccountService {
@Override
public Long saveOrUpdate(BanmaAccount entity) {
return saveOrUpdate(entity, null);
}
@Override
public Long saveOrUpdate(BanmaAccount entity, String clientUsername) {
// 设置客户端用户名
if (clientUsername != null) {
entity.setClientUsername(clientUsername);
}
if (entity.getId() == null) {
mapper.insert(entity);
} else {
mapper.update(entity);
}
if (Objects.equals(entity.getIsDefault(), 1)) {
mapper.clearDefault();
// 如果设为默认,则清除该客户端的其他默认账号
if (Objects.equals(entity.getIsDefault(), 1) && entity.getClientUsername() != null) {
mapper.clearDefault(entity.getClientUsername());
BanmaAccount only = new BanmaAccount();
only.setId(entity.getId());
only.setIsDefault(1);

View File

@@ -7,6 +7,7 @@
<result property="name" column="name"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="clientUsername" column="client_username"/>
<result property="token" column="token"/>
<result property="tokenExpireAt" column="token_expire_at"/>
<result property="isDefault" column="is_default"/>
@@ -19,7 +20,7 @@
</resultMap>
<sql id="Base_Column_List">
id, name, username, password, token, token_expire_at, is_default, status, remark, create_by, create_time, update_by, update_time
id, name, username, password, client_username, token, token_expire_at, is_default, status, remark, create_by, create_time, update_by, update_time
</sql>
<select id="selectById" parameterType="long" resultMap="BanmaAccountResult">
@@ -29,6 +30,7 @@
<select id="selectList" parameterType="com.ruoyi.system.domain.BanmaAccount" resultMap="BanmaAccountResult">
select <include refid="Base_Column_List"/> from banma_account
<where>
<if test="clientUsername != null and clientUsername != ''"> and client_username = #{clientUsername}</if>
<if test="name != null and name != ''"> and name like concat('%', #{name}, '%')</if>
<if test="username != null and username != ''"> and username like concat('%', #{username}, '%')</if>
<if test="status != null"> and status = #{status}</if>
@@ -42,6 +44,7 @@
<if test="name != null">name,</if>
<if test="username != null">username,</if>
<if test="password != null">password,</if>
<if test="clientUsername != null">client_username,</if>
<if test="token != null">token,</if>
<if test="tokenExpireAt != null">token_expire_at,</if>
<if test="isDefault != null">is_default,</if>
@@ -54,6 +57,7 @@
<if test="name != null">#{name},</if>
<if test="username != null">#{username},</if>
<if test="password != null">#{password},</if>
<if test="clientUsername != null">#{clientUsername},</if>
<if test="token != null">#{token},</if>
<if test="tokenExpireAt != null">#{tokenExpireAt},</if>
<if test="isDefault != null">#{isDefault},</if>
@@ -70,6 +74,7 @@
<if test="name != null">name=#{name},</if>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="clientUsername != null">client_username=#{clientUsername},</if>
<if test="token != null">token=#{token},</if>
<if test="tokenExpireAt != null">token_expire_at=#{tokenExpireAt},</if>
<if test="isDefault != null">is_default=#{isDefault},</if>
@@ -82,7 +87,10 @@
</update>
<update id="clearDefault">
update banma_account set is_default = 0 where is_default = 1
update banma_account set is_default = 0 where is_default = 1
<if test="clientUsername != null and clientUsername != ''">
and client_username = #{clientUsername}
</if>
</update>
<delete id="deleteById" parameterType="long">

View File

@@ -15,6 +15,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="remark" column="remark" />
<result property="permissions" column="permissions" />
<result property="deviceLimit" column="device_limit" />
<result property="accountType" column="account_type" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
@@ -23,7 +24,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="selectClientAccountVo">
select id, account_name, username, password, status, expire_time,
allowed_ip_range, remark, permissions, device_limit, create_by, create_time, update_by, update_time
allowed_ip_range, remark, permissions, device_limit, account_type, create_by, create_time, update_by, update_time
from client_account
</sql>
@@ -59,6 +60,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="remark != null">remark,</if>
<if test="permissions != null">permissions,</if>
<if test="deviceLimit != null">device_limit,</if>
<if test="accountType != null">account_type,</if>
<if test="createBy != null">create_by,</if>
create_time
</trim>
@@ -72,6 +74,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="remark != null">#{remark},</if>
<if test="permissions != null">#{permissions},</if>
<if test="deviceLimit != null">#{deviceLimit},</if>
<if test="accountType != null">#{accountType},</if>
<if test="createBy != null">#{createBy},</if>
sysdate()
</trim>
@@ -89,6 +92,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="remark != null">remark = #{remark},</if>
<if test="permissions != null">permissions = #{permissions},</if>
<if test="deviceLimit != null">device_limit = #{deviceLimit},</if>
<if test="accountType != null">account_type = #{accountType},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
update_time = sysdate()
</trim>

View File

@@ -11,6 +11,7 @@
<result property="ip" column="ip"/>
<result property="location" column="location"/>
<result property="lastActiveAt" column="last_active_at"/>
<result property="trialExpireTime" column="trial_expire_time"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
@@ -19,13 +20,17 @@
select * from client_device where device_id = #{deviceId}
</select>
<select id="selectByDeviceIdAndUsername" resultMap="ClientDeviceMap">
select * from client_device where device_id = #{deviceId} and username = #{username}
</select>
<select id="selectByUsername" resultMap="ClientDeviceMap">
select * from client_device where username = #{username} and status != 'removed' order by update_time desc
</select>
<insert id="insert" parameterType="com.ruoyi.system.domain.ClientDevice" useGeneratedKeys="true" keyProperty="id">
insert into client_device(username, device_id, name, os, status, ip, location, last_active_at, create_time, update_time)
values(#{username}, #{deviceId}, #{name}, #{os}, #{status}, #{ip}, #{location}, #{lastActiveAt}, now(), now())
insert into client_device(username, device_id, name, os, status, ip, location, last_active_at, trial_expire_time, create_time, update_time)
values(#{username}, #{deviceId}, #{name}, #{os}, #{status}, #{ip}, #{location}, #{lastActiveAt}, #{trialExpireTime}, now(), now())
</insert>
<update id="updateByDeviceId" parameterType="com.ruoyi.system.domain.ClientDevice">
@@ -37,10 +42,26 @@
ip = #{ip},
location = #{location},
last_active_at = #{lastActiveAt},
trial_expire_time = #{trialExpireTime},
update_time = now()
where device_id = #{deviceId}
</update>
<update id="updateByDeviceIdAndUsername" parameterType="com.ruoyi.system.domain.ClientDevice">
update client_device
<set>
<if test="name != null">name = #{name},</if>
<if test="os != null">os = #{os},</if>
<if test="status != null">status = #{status},</if>
<if test="ip != null">ip = #{ip},</if>
<if test="location != null">location = #{location},</if>
<if test="lastActiveAt != null">last_active_at = #{lastActiveAt},</if>
<if test="trialExpireTime != null">trial_expire_time = #{trialExpireTime},</if>
update_time = now()
</set>
where device_id = #{deviceId} and username = #{username}
</update>
<delete id="deleteByDeviceId">
delete from client_device where device_id = #{deviceId}
</delete>