feat(genmai): 集成跟卖精灵账号管理系统跟

- 新增卖精灵账号管理功能,支持多账号切换
- 实现账号增删改查接口与前端交互逻辑
-优化打开跟卖精灵流程,增加账号选择界面
- 添加账号权限限制与订阅升级提醒
- 完善后端账号实体类及数据库映射
- 更新系统控制器以支持指定账号启动功能- 调整HTTP请求路径适配新工具模块路由- 升级客户端版本至2.5.3并优化代码结构
This commit is contained in:
2025-10-27 09:13:00 +08:00
parent 35c9fc205a
commit 0be60bc103
18 changed files with 720 additions and 132 deletions

View File

@@ -0,0 +1,51 @@
package com.ruoyi.system.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
/**
* 跟卖精灵账号实体(数据库持久化)
*/
public class GenmaiAccount extends BaseEntity {
private static final long serialVersionUID = 1L;
private Long id;
/** 显示名 */
private String name;
/** 登录用户名nickname */
private String username;
/** 登录密码服务端刷新Token用不对外返回 */
private String password;
/** 客户端账号用户名关联client_account.username */
private String clientUsername;
/** 访问 Token跨设备共享 */
private String token;
/** Token 过期时间(可选) */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date tokenExpireAt;
/** 状态 1启用 0停用 */
private Integer status;
/** 备注 */
private String remark;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getUsername() { return username; }
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; }
public void setTokenExpireAt(Date tokenExpireAt) { this.tokenExpireAt = tokenExpireAt; }
public Integer getStatus() { return status; }
public void setStatus(Integer status) { this.status = status; }
@Override public String getRemark() { return remark; }
@Override public void setRemark(String remark) { this.remark = remark; }
}

View File

@@ -0,0 +1,18 @@
package com.ruoyi.system.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.system.domain.GenmaiAccount;
/**
* 跟卖精灵账号 Mapper
*/
public interface GenmaiAccountMapper {
GenmaiAccount selectById(Long id);
List<GenmaiAccount> selectList(GenmaiAccount query);
int insert(GenmaiAccount entity);
int update(GenmaiAccount entity);
int deleteById(Long id);
int deleteByClientUsername(@Param("clientUsername") String clientUsername);
}

View File

@@ -0,0 +1,50 @@
package com.ruoyi.system.service;
import java.util.List;
import com.ruoyi.system.domain.GenmaiAccount;
/**
* 跟卖精灵账号 Service 接口
*/
public interface IGenmaiAccountService {
/**
* 获取所有账号(简化信息,密码置空)
*/
List<GenmaiAccount> listSimple();
/**
* 获取指定客户端用户的账号列表
*/
List<GenmaiAccount> listSimple(String clientUsername);
/**
* 保存或更新账号
*/
Long saveOrUpdate(GenmaiAccount entity);
/**
* 保存或更新账号(带客户端用户名)
*/
Long saveOrUpdate(GenmaiAccount entity, String clientUsername);
/**
* 删除账号
*/
void remove(Long id);
/**
* 验证token是否有效调用跟卖精灵接口
*/
boolean validateToken(String token);
/**
* 刷新token仅在token失效时
*/
boolean refreshTokenIfNeeded(Long id);
/**
* 验证账号密码并获取Token不保存到数据库
*/
String validateAndGetToken(String username, String password);
}

View File

@@ -0,0 +1,116 @@
package com.ruoyi.system.service.impl;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.system.domain.GenmaiAccount;
import com.ruoyi.system.domain.ClientAccount;
import com.ruoyi.system.mapper.GenmaiAccountMapper;
import com.ruoyi.system.mapper.ClientAccountMapper;
import com.ruoyi.system.service.IGenmaiAccountService;
@Service
public class GenmaiAccountServiceImpl implements IGenmaiAccountService {
@Autowired
private GenmaiAccountMapper mapper;
@Autowired
private ClientAccountMapper clientAccountMapper;
private final RestTemplate restTemplate = new RestTemplate();
private final ObjectMapper objectMapper = new ObjectMapper();
private int getAccountLimit(String clientUsername) {
ClientAccount client = clientAccountMapper.selectClientAccountByUsername(clientUsername);
return "paid".equals(client.getAccountType()) && new Date().before(client.getExpireTime()) ? 3 : 1;
}
@Override
public List<GenmaiAccount> listSimple() {
return listSimple(null);
}
@Override
public List<GenmaiAccount> listSimple(String clientUsername) {
GenmaiAccount query = new GenmaiAccount();
query.setClientUsername(clientUsername);
List<GenmaiAccount> list = mapper.selectList(query);
list.forEach(a -> a.setPassword(null));
return list;
}
@Override
public Long saveOrUpdate(GenmaiAccount entity) {
return saveOrUpdate(entity, null);
}
@Override
public Long saveOrUpdate(GenmaiAccount entity, String clientUsername) {
entity.setClientUsername(clientUsername);
if (entity.getId() == null) {
int count = mapper.selectList(entity).size();
if (count >= getAccountLimit(clientUsername)) {
throw new RuntimeException("账号数量已达上限");
}
mapper.insert(entity);
} else {
mapper.update(entity);
}
return entity.getId();
}
@Override
public void remove(Long id) {
mapper.deleteById(id);
}
@Override
public boolean validateToken(String token) {
try {
HttpEntity<String> entity = new HttpEntity<>(null, null);
ResponseEntity<String> response = restTemplate.exchange(
"https://www.genmaijl.com/manage/user/balance",
HttpMethod.GET, entity, String.class);
return objectMapper.readTree(response.getBody()).get("code").asInt() == 0;
} catch (Exception e) {
return false;
}
}
@Override
public boolean refreshTokenIfNeeded(Long id) {
GenmaiAccount account = mapper.selectById(id);
if (validateToken(account.getToken())) return true;
String token = validateAndGetToken(account.getUsername(), account.getPassword());
account.setToken(token);
account.setTokenExpireAt(new Date(System.currentTimeMillis() + 30L * 24 * 60 * 60 * 1000));
mapper.update(account);
return true;
}
@Override
public String validateAndGetToken(String username, String password) {
try {
Map<String, String> body = new HashMap<>();
body.put("nickname", username);
body.put("password", password);
ResponseEntity<String> response = restTemplate.exchange(
"https://www.genmaijl.com/manage/user/subLogin",
HttpMethod.POST, new HttpEntity<>(body), String.class);
return objectMapper.readTree(response.getBody()).get("result").asText();
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,94 @@
<?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.ruoyi.system.mapper.GenmaiAccountMapper">
<resultMap id="GenmaiAccountResult" type="com.ruoyi.system.domain.GenmaiAccount">
<result property="id" column="id"/>
<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="status" column="status"/>
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<sql id="Base_Column_List">
id, name, username, password, client_username, token, token_expire_at, status, remark, create_by, create_time, update_by, update_time
</sql>
<select id="selectById" parameterType="long" resultMap="GenmaiAccountResult">
select <include refid="Base_Column_List"/> from genmai_account where id = #{id}
</select>
<select id="selectList" parameterType="com.ruoyi.system.domain.GenmaiAccount" resultMap="GenmaiAccountResult">
select <include refid="Base_Column_List"/> from genmai_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>
</where>
order by create_time desc
</select>
<insert id="insert" parameterType="com.ruoyi.system.domain.GenmaiAccount" useGeneratedKeys="true" keyProperty="id">
insert into genmai_account
<trim prefix="(" suffix=")" suffixOverrides=",">
<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="status != null">status,</if>
<if test="remark != null">remark,</if>
<if test="createBy != null">create_by,</if>
create_time
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<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="status != null">#{status},</if>
<if test="remark != null">#{remark},</if>
<if test="createBy != null">#{createBy},</if>
sysdate()
</trim>
</insert>
<update id="update" parameterType="com.ruoyi.system.domain.GenmaiAccount">
update genmai_account
<trim prefix="set" suffixOverrides=",">
<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="status != null">status=#{status},</if>
<if test="remark != null">remark=#{remark},</if>
<if test="updateBy != null">update_by=#{updateBy},</if>
update_time = sysdate()
</trim>
where id = #{id}
</update>
<delete id="deleteById" parameterType="long">
delete from genmai_account where id = #{id}
</delete>
<delete id="deleteByClientUsername">
delete from genmai_account where client_username = #{clientUsername}
</delete>
</mapper>