SHA256
Добавлены родственные связи, расширен граф связей и улучшен локальный запуск
Что добавлено:\n- Новые типы CONNECTION для родственников: parent/child/sibling (50/51, 52/53, 54/55) в blockchain/db слоях.\n- Обновлены проверки ConnectionBody и DB-триггер connections_state для корректной записи/удаления новых связей.\n- В профиле добавлен блок "Близкие родственники" с модальным выбором типа связи и логина; добавление через AddBlock для parent/child/sibling.\n- Расширен API GetUserConnectionsGraph: out/in списки для родителей/детей/сиблингов, агрегированные списки родственников с полом, список allUsers с метками официальный/сияющий.\n- Полностью обновлен UI страницы "Связи": новое позиционирование родственников вокруг центра, отдельный цвет родственных связей, линия для взаимных связей и стрелка для односторонних, корректная геометрия линий при ресайзе.\n- Добавлена Gradle-задача startLocalWithBuild для запуска локального стека после build; сохранена отдельная startLocal без полного build.
This commit is contained in:
+178
@@ -14,7 +14,13 @@ import shine.db.dao.ConnectionsStateDAO;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class Net_GetUserConnectionsGraph_Handler implements JsonMessageHandler {
|
||||
@Override
|
||||
@@ -37,6 +43,22 @@ public class Net_GetUserConnectionsGraph_Handler implements JsonMessageHandler {
|
||||
List<String> inContacts = ConnectionsStateDAO.getInstance().listIncomingByRelTypeCanonical(c, canonicalLogin, MsgSubType.CONNECTION_CONTACT);
|
||||
List<String> outFollows = ConnectionsStateDAO.getInstance().listOutgoingByRelTypeCanonical(c, canonicalLogin, MsgSubType.CONNECTION_FOLLOW);
|
||||
List<String> inFollows = ConnectionsStateDAO.getInstance().listIncomingByRelTypeCanonical(c, canonicalLogin, MsgSubType.CONNECTION_FOLLOW);
|
||||
List<String> outParents = ConnectionsStateDAO.getInstance().listOutgoingByRelTypeCanonical(c, canonicalLogin, MsgSubType.CONNECTION_PARENT);
|
||||
List<String> inParents = ConnectionsStateDAO.getInstance().listIncomingByRelTypeCanonical(c, canonicalLogin, MsgSubType.CONNECTION_PARENT);
|
||||
List<String> outChildren = ConnectionsStateDAO.getInstance().listOutgoingByRelTypeCanonical(c, canonicalLogin, MsgSubType.CONNECTION_CHILD);
|
||||
List<String> inChildren = ConnectionsStateDAO.getInstance().listIncomingByRelTypeCanonical(c, canonicalLogin, MsgSubType.CONNECTION_CHILD);
|
||||
List<String> outSiblings = ConnectionsStateDAO.getInstance().listOutgoingByRelTypeCanonical(c, canonicalLogin, MsgSubType.CONNECTION_SIBLING);
|
||||
List<String> inSiblings = ConnectionsStateDAO.getInstance().listIncomingByRelTypeCanonical(c, canonicalLogin, MsgSubType.CONNECTION_SIBLING);
|
||||
|
||||
LinkedHashSet<String> allLogins = new LinkedHashSet<>();
|
||||
allLogins.add(canonicalLogin);
|
||||
addAllLogins(allLogins, outFriends, inFriends, outContacts, inContacts, outFollows, inFollows,
|
||||
outParents, inParents, outChildren, inChildren, outSiblings, inSiblings);
|
||||
|
||||
Map<String, UserMeta> metaByLogin = loadUserMeta(c, allLogins);
|
||||
List<String> parentLogins = mergeUnique(outParents, inChildren);
|
||||
List<String> childLogins = mergeUnique(outChildren, inParents);
|
||||
List<String> siblingLogins = mergeUnique(outSiblings, inSiblings);
|
||||
|
||||
Net_GetUserConnectionsGraph_Response resp = new Net_GetUserConnectionsGraph_Response();
|
||||
resp.setOp(req.getOp());
|
||||
@@ -49,6 +71,16 @@ public class Net_GetUserConnectionsGraph_Handler implements JsonMessageHandler {
|
||||
resp.setInContacts(inContacts);
|
||||
resp.setOutFollows(outFollows);
|
||||
resp.setInFollows(inFollows);
|
||||
resp.setOutParents(outParents);
|
||||
resp.setInParents(inParents);
|
||||
resp.setOutChildren(outChildren);
|
||||
resp.setInChildren(inChildren);
|
||||
resp.setOutSiblings(outSiblings);
|
||||
resp.setInSiblings(inSiblings);
|
||||
resp.setParents(toRelativeItems(parentLogins, metaByLogin));
|
||||
resp.setChildren(toRelativeItems(childLogins, metaByLogin));
|
||||
resp.setSiblings(toRelativeItems(siblingLogins, metaByLogin));
|
||||
resp.setAllUsers(toUserMarkItems(allLogins, metaByLogin));
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
@@ -62,4 +94,150 @@ public class Net_GetUserConnectionsGraph_Handler implements JsonMessageHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private final void addAllLogins(Set<String> target, List<String>... lists) {
|
||||
if (target == null || lists == null) return;
|
||||
for (List<String> list : lists) {
|
||||
if (list == null) continue;
|
||||
for (String login : list) {
|
||||
String clean = (login == null) ? "" : login.trim();
|
||||
if (!clean.isEmpty()) target.add(clean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> mergeUnique(List<String> first, List<String> second) {
|
||||
LinkedHashMap<String, String> byKey = new LinkedHashMap<>();
|
||||
addToMergeMap(byKey, first);
|
||||
addToMergeMap(byKey, second);
|
||||
return new ArrayList<>(byKey.values());
|
||||
}
|
||||
|
||||
private void addToMergeMap(Map<String, String> target, List<String> source) {
|
||||
if (target == null || source == null) return;
|
||||
for (String login : source) {
|
||||
String clean = (login == null) ? "" : login.trim();
|
||||
if (clean.isEmpty()) continue;
|
||||
String key = normKey(clean);
|
||||
if (!target.containsKey(key)) target.put(key, clean);
|
||||
}
|
||||
}
|
||||
|
||||
private String normKey(String login) {
|
||||
return String.valueOf(login == null ? "" : login).trim().toLowerCase();
|
||||
}
|
||||
|
||||
private Map<String, UserMeta> loadUserMeta(Connection c, Set<String> logins) throws Exception {
|
||||
Map<String, UserMeta> out = new HashMap<>();
|
||||
if (logins == null || logins.isEmpty()) return out;
|
||||
|
||||
String[] placeholders = new String[logins.size()];
|
||||
for (int i = 0; i < placeholders.length; i += 1) placeholders[i] = "?";
|
||||
String sql = """
|
||||
SELECT
|
||||
su.login AS login,
|
||||
MAX(CASE WHEN up.param = 'gender' THEN up.value END) AS gender_value,
|
||||
MAX(CASE WHEN up.param = 'official' THEN up.value END) AS official_value,
|
||||
MAX(CASE WHEN up.param = 'shine' THEN up.value END) AS shine_value
|
||||
FROM solana_users su
|
||||
LEFT JOIN users_params up
|
||||
ON up.login = su.login COLLATE NOCASE
|
||||
AND up.param IN ('gender', 'official', 'shine')
|
||||
WHERE su.login COLLATE NOCASE IN (%s)
|
||||
GROUP BY su.login
|
||||
ORDER BY su.login
|
||||
""".formatted(String.join(", ", placeholders));
|
||||
|
||||
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||
int i = 1;
|
||||
for (String login : logins) {
|
||||
ps.setString(i, login);
|
||||
i += 1;
|
||||
}
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
String login = rs.getString("login");
|
||||
if (login == null || login.isBlank()) continue;
|
||||
UserMeta meta = new UserMeta();
|
||||
meta.gender = normalizeGender(rs.getString("gender_value"));
|
||||
meta.official = parseToggle(rs.getString("official_value"));
|
||||
meta.shine = parseToggle(rs.getString("shine_value"));
|
||||
out.put(normKey(login), meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private boolean parseToggle(String rawValue) {
|
||||
String v = String.valueOf(rawValue == null ? "" : rawValue).trim().toLowerCase();
|
||||
return "1".equals(v) || "yes".equals(v) || "true".equals(v) || "on".equals(v);
|
||||
}
|
||||
|
||||
private String normalizeGender(String rawValue) {
|
||||
String v = String.valueOf(rawValue == null ? "" : rawValue).trim().toLowerCase();
|
||||
if ("male".equals(v)) return "male";
|
||||
if ("female".equals(v)) return "female";
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private String genderLabel(String gender) {
|
||||
if ("male".equals(gender)) return "мужчина";
|
||||
if ("female".equals(gender)) return "женщина";
|
||||
return "не указан";
|
||||
}
|
||||
|
||||
private List<Net_GetUserConnectionsGraph_Response.RelativeItem> toRelativeItems(
|
||||
List<String> logins,
|
||||
Map<String, UserMeta> metaByLogin
|
||||
) {
|
||||
List<Net_GetUserConnectionsGraph_Response.RelativeItem> items = new ArrayList<>();
|
||||
if (logins == null) return items;
|
||||
for (String login : logins) {
|
||||
String clean = (login == null) ? "" : login.trim();
|
||||
if (clean.isEmpty()) continue;
|
||||
UserMeta meta = metaByLogin.get(normKey(clean));
|
||||
String gender = (meta == null) ? "unknown" : meta.gender;
|
||||
|
||||
Net_GetUserConnectionsGraph_Response.RelativeItem it = new Net_GetUserConnectionsGraph_Response.RelativeItem();
|
||||
it.setLogin(clean);
|
||||
it.setGender(gender);
|
||||
it.setGenderLabel(genderLabel(gender));
|
||||
items.add(it);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<Net_GetUserConnectionsGraph_Response.UserMarkItem> toUserMarkItems(
|
||||
Set<String> logins,
|
||||
Map<String, UserMeta> metaByLogin
|
||||
) {
|
||||
List<Net_GetUserConnectionsGraph_Response.UserMarkItem> items = new ArrayList<>();
|
||||
if (logins == null) return items;
|
||||
|
||||
for (String login : logins) {
|
||||
String clean = (login == null) ? "" : login.trim();
|
||||
if (clean.isEmpty()) continue;
|
||||
|
||||
UserMeta meta = metaByLogin.get(normKey(clean));
|
||||
boolean official = meta != null && meta.official;
|
||||
boolean shine = meta != null && meta.shine;
|
||||
|
||||
Net_GetUserConnectionsGraph_Response.UserMarkItem it = new Net_GetUserConnectionsGraph_Response.UserMarkItem();
|
||||
it.setLogin(clean);
|
||||
it.setOfficial(official);
|
||||
it.setShine(shine);
|
||||
it.setOfficialLabel(official ? "официальный" : "неофициальный");
|
||||
it.setShineLabel(shine ? "сияющий" : "несияющий");
|
||||
items.add(it);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private static final class UserMeta {
|
||||
private String gender = "unknown";
|
||||
private boolean official = false;
|
||||
private boolean shine = false;
|
||||
}
|
||||
}
|
||||
|
||||
+62
@@ -13,6 +13,48 @@ public class Net_GetUserConnectionsGraph_Response extends Net_Response {
|
||||
private List<String> inContacts = new ArrayList<>();
|
||||
private List<String> outFollows = new ArrayList<>();
|
||||
private List<String> inFollows = new ArrayList<>();
|
||||
private List<String> outParents = new ArrayList<>();
|
||||
private List<String> inParents = new ArrayList<>();
|
||||
private List<String> outChildren = new ArrayList<>();
|
||||
private List<String> inChildren = new ArrayList<>();
|
||||
private List<String> outSiblings = new ArrayList<>();
|
||||
private List<String> inSiblings = new ArrayList<>();
|
||||
private List<RelativeItem> parents = new ArrayList<>();
|
||||
private List<RelativeItem> children = new ArrayList<>();
|
||||
private List<RelativeItem> siblings = new ArrayList<>();
|
||||
private List<UserMarkItem> allUsers = new ArrayList<>();
|
||||
|
||||
public static class RelativeItem {
|
||||
private String login;
|
||||
private String gender;
|
||||
private String genderLabel;
|
||||
|
||||
public String getLogin() { return login; }
|
||||
public void setLogin(String login) { this.login = login; }
|
||||
public String getGender() { return gender; }
|
||||
public void setGender(String gender) { this.gender = gender; }
|
||||
public String getGenderLabel() { return genderLabel; }
|
||||
public void setGenderLabel(String genderLabel) { this.genderLabel = genderLabel; }
|
||||
}
|
||||
|
||||
public static class UserMarkItem {
|
||||
private String login;
|
||||
private boolean official;
|
||||
private boolean shine;
|
||||
private String officialLabel;
|
||||
private String shineLabel;
|
||||
|
||||
public String getLogin() { return login; }
|
||||
public void setLogin(String login) { this.login = login; }
|
||||
public boolean isOfficial() { return official; }
|
||||
public void setOfficial(boolean official) { this.official = official; }
|
||||
public boolean isShine() { return shine; }
|
||||
public void setShine(boolean shine) { this.shine = shine; }
|
||||
public String getOfficialLabel() { return officialLabel; }
|
||||
public void setOfficialLabel(String officialLabel) { this.officialLabel = officialLabel; }
|
||||
public String getShineLabel() { return shineLabel; }
|
||||
public void setShineLabel(String shineLabel) { this.shineLabel = shineLabel; }
|
||||
}
|
||||
|
||||
public String getLogin() { return login; }
|
||||
public void setLogin(String login) { this.login = login; }
|
||||
@@ -28,4 +70,24 @@ public class Net_GetUserConnectionsGraph_Response extends Net_Response {
|
||||
public void setOutFollows(List<String> outFollows) { this.outFollows = outFollows; }
|
||||
public List<String> getInFollows() { return inFollows; }
|
||||
public void setInFollows(List<String> inFollows) { this.inFollows = inFollows; }
|
||||
public List<String> getOutParents() { return outParents; }
|
||||
public void setOutParents(List<String> outParents) { this.outParents = outParents; }
|
||||
public List<String> getInParents() { return inParents; }
|
||||
public void setInParents(List<String> inParents) { this.inParents = inParents; }
|
||||
public List<String> getOutChildren() { return outChildren; }
|
||||
public void setOutChildren(List<String> outChildren) { this.outChildren = outChildren; }
|
||||
public List<String> getInChildren() { return inChildren; }
|
||||
public void setInChildren(List<String> inChildren) { this.inChildren = inChildren; }
|
||||
public List<String> getOutSiblings() { return outSiblings; }
|
||||
public void setOutSiblings(List<String> outSiblings) { this.outSiblings = outSiblings; }
|
||||
public List<String> getInSiblings() { return inSiblings; }
|
||||
public void setInSiblings(List<String> inSiblings) { this.inSiblings = inSiblings; }
|
||||
public List<RelativeItem> getParents() { return parents; }
|
||||
public void setParents(List<RelativeItem> parents) { this.parents = parents; }
|
||||
public List<RelativeItem> getChildren() { return children; }
|
||||
public void setChildren(List<RelativeItem> children) { this.children = children; }
|
||||
public List<RelativeItem> getSiblings() { return siblings; }
|
||||
public void setSiblings(List<RelativeItem> siblings) { this.siblings = siblings; }
|
||||
public List<UserMarkItem> getAllUsers() { return allUsers; }
|
||||
public void setAllUsers(List<UserMarkItem> allUsers) { this.allUsers = allUsers; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user