diff --git a/adapters/java/pom.xml b/adapters/java/pom.xml new file mode 100644 index 0000000..db54d10 --- /dev/null +++ b/adapters/java/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + cn.uid9622.longhun + lh-standard-adapter + 1.0.0 + LongHun Standard Adapter + v∞ DNA traceability + seven-factor behavioral audit for Java/Kotlin + + 8 + 8 + UTF-8 + + + com.fasterxml.jackson.corejackson-databind2.15.3 + org.junit.jupiterjunit-jupiter5.10.0test + + org.apache.maven.pluginsmaven-surefire-plugin3.1.2 + diff --git a/adapters/java/src/main/java/cn/uid9622/longhun/AuditWrapper.java b/adapters/java/src/main/java/cn/uid9622/longhun/AuditWrapper.java new file mode 100644 index 0000000..314682c --- /dev/null +++ b/adapters/java/src/main/java/cn/uid9622/longhun/AuditWrapper.java @@ -0,0 +1,85 @@ +package cn.uid9622.longhun; + +import java.security.MessageDigest; +import java.time.ZonedDateTime; +import java.time.ZoneOffset; +import java.util.*; + +public class AuditWrapper { + private static final Map> LABEL_MAP = new LinkedHashMap<>(); + static { + LABEL_MAP.put("P", Map.of("HasPromise","7F-P-有承诺","NoPromise","7F-P-无承诺")); + LABEL_MAP.put("F", Map.of("Fulfilled","7F-F-已兑现","Unfulfilled","7F-F-未兑现","Partial","7F-F-部分兑现")); + LABEL_MAP.put("E", Map.of("Willing","7F-E-心甘情愿","Perfunctory","7F-E-敷衍","Resentful","7F-E-怨恨","Numb","7F-E-麻木")); + LABEL_MAP.put("A", Map.of("Self","7F-A-自己","Partner","7F-A-伴侣","Family","7F-A-家庭","Outsider","7F-A-外人","Public","7F-A-公众")); + LABEL_MAP.put("X", Map.of("OverExplain","7F-X-过度解释","Silent","7F-X-沉默","Genuine","7F-X-真诚","Indifferent","7F-X-冷漠")); + LABEL_MAP.put("Y", Map.of("Changed","7F-Y-改正","Resisted","7F-Y-抗拒","Indifferent","7F-Y-无视","NoResponse","7F-Y-无响应")); + } + + private final String uid; + public AuditWrapper(String uid) { this.uid = uid != null ? uid : "9622"; } + + public Map wrap(Object payload, String taskType, String persona) { + if (taskType == null || taskType.isEmpty()) taskType = "default"; + if (persona == null || persona.isEmpty()) persona = "P04"; + + Map sig = new LinkedHashMap<>(); + sig.put("P","HasPromise"); sig.put("F","Fulfilled"); sig.put("T",0.0); + sig.put("E","Willing"); sig.put("C",0); sig.put("R",0); + sig.put("A","Self"); sig.put("X","Genuine"); sig.put("Y","NoResponse"); sig.put("Z",1.0); + + String pattern = classify(sig); + List labels = makeLabels(sig, pattern); + String color = determineColor(pattern, (int)sig.get("R")); + + Map result = new LinkedHashMap<>(); + result.put("audit_version","v1.0"); + result.put("uid","UID"+uid); + result.put("persona",persona); + result.put("task_type",taskType); + result.put("behavior_signature",sig); + result.put("behavior_pattern",pattern); + result.put("behavior_labels",labels); + result.put("color",color); + result.put("timestamp", ZonedDateTime.now(ZoneOffset.UTC).toString()); + + try { + String pj = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(payload); + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] h = md.digest(pj.getBytes("UTF-8")); + StringBuilder sb = new StringBuilder(); + for (byte b : h) sb.append(String.format("%02x", b)); + result.put("payload_hash", sb.toString().substring(0,16)); + } catch (Exception e) { result.put("payload_hash","0000000000000000"); } + return result; + } + + static String classify(Map sig) { + String f = (String)sig.get("F"), x = (String)sig.get("X"); + String a = (String)sig.get("A"), y = (String)sig.get("Y"); + double z = ((Number)sig.getOrDefault("Z",0.0)).doubleValue(); + if ("Unfulfilled".equals(f) && "OverExplain".equals(x)) return "MODE-DefensiveDefaulter"; + if ("Fulfilled".equals(f) && "Outsider".equals(a)) return "MODE-ExternalTrustSpender"; + if ("Unfulfilled".equals(f) && "Indifferent".equals(y)) return "MODE-InternalDestroyer"; + if (z > 2.0) return "MODE-Fluctuating"; + return "MODE-StableDisciplined"; + } + + static List makeLabels(Map sig, String pattern) { + List labels = new ArrayList<>(); + for (String factor : new String[]{"P","F","E","A","X","Y"}) { + String val = (String)sig.get(factor); + if (val != null && LABEL_MAP.containsKey(factor) && LABEL_MAP.get(factor).containsKey(val)) + labels.add(LABEL_MAP.get(factor).get(val)); + } + labels.add(pattern); + return labels; + } + + static String determineColor(String pattern, int repeat) { + if ("MODE-InternalDestroyer".equals(pattern)) return "🔴"; + if ("MODE-Fluctuating".equals(pattern) && repeat > 3) return "🟡"; + if ("MODE-DefensiveDefaulter".equals(pattern) && repeat > 2) return "🟡"; + return "🟢"; + } +} diff --git a/adapters/java/src/main/java/cn/uid9622/longhun/DNAGenerator.java b/adapters/java/src/main/java/cn/uid9622/longhun/DNAGenerator.java new file mode 100644 index 0000000..59f965c --- /dev/null +++ b/adapters/java/src/main/java/cn/uid9622/longhun/DNAGenerator.java @@ -0,0 +1,93 @@ +package cn.uid9622.longhun; + +import java.security.MessageDigest; +import java.time.ZonedDateTime; +import java.time.ZoneOffset; +import java.util.*; + +public class DNAGenerator { + private static final String[] TIAN_GAN = {"Jia","Yi","Bing","Ding","Wu","Ji","Geng","Xin","Ren","Gui"}; + private static final String[] DI_ZHI = {"Zi","Chou","Yin","Mao","Chen","Si","Wu","Wei","Shen","You","Xu","Hai"}; + private static final String[] SHI_CHEN = {"ZiShi","ChouShi","YinShi","MaoShi","ChenShi","SiShi","WuShi","WeiShi","ShenShi","YouShi","XuShi","HaiShi"}; + + private static final int[] CYCLE_MONTH = {2,4,6,8,10,0,2,4,6,8,10,0}; + private static final int CYCLE_YEAR = 1984; + + private final String uid, device, locale; + + public DNAGenerator(String uid, String device, String locale) { + this.uid = uid != null ? uid : "9622"; + this.device = device != null ? device : "HM-9622-001"; + this.locale = locale != null ? locale : "Asia/Shanghai"; + } + + public String generate(String taskType, String action, String version) { + if (taskType == null || taskType.isEmpty()) taskType = "default"; + if (action == null || action.isEmpty()) action = "WRAP"; + if (version == null || version.isEmpty()) version = "V1.0"; + + ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); + Map stem = computeStemBranch(now); + String[] hex = selectHexagram(taskType); + String body = "ADAPTER-" + taskType.toUpperCase() + "-" + action.toUpperCase() + "-" + version; + + String raw = stem.get("year") + stem.get("month") + stem.get("day") + stem.get("shichen") + + hex[0] + hex[1] + body + device + now.toString(); + + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] hash = md.digest(raw.getBytes("UTF-8")); + StringBuilder sb = new StringBuilder(); + for (byte b : hash) sb.append(String.format("%02x", b)); + String hash8 = sb.toString().substring(0, 8); + + return "#LongHun⚡️" + stem.get("year") + "·" + stem.get("month") + "·" + stem.get("day") + + "·" + stem.get("shichen") + "·" + hex[0] + hex[1] + "-" + body + "-" + hash8; + } catch (Exception e) { return "#LongHun⚡️ERROR-" + e.getMessage(); } + } + + private Map computeStemBranch(ZonedDateTime dt) { + int y = dt.getYear(), m = dt.getMonthValue(), doy = dt.getDayOfYear(), h = dt.getHour(); + int ys = ((y - CYCLE_YEAR) % 10 + 10) % 10; + int yb = ((y - CYCLE_YEAR) % 12 + 12) % 12; + int mb = CYCLE_MONTH[((y - CYCLE_YEAR) % 10 + 10) % 10]; + int ms = ((mb + m - 1) % 10 + 10) % 10; + int mbr = ((m + 1) % 12 + 12) % 12; + int ds = ((y - 1900 + (y - 1900) / 4 + doy) % 10 + 10) % 10; + int db = ((y - 1900 + (y - 1900) / 4 + doy) % 12 + 12) % 12; + int si = h / 2; + + Map result = new LinkedHashMap<>(); + result.put("year", TIAN_GAN[ys] + DI_ZHI[yb]); + result.put("month", TIAN_GAN[ms] + DI_ZHI[mbr]); + result.put("day", TIAN_GAN[ds] + DI_ZHI[db]); + result.put("shichen", SHI_CHEN[si]); + return result; + } + + private String[] selectHexagram(String taskType) { + String domain; + switch (taskType) { + case "code": domain = "engine"; break; + case "deploy": domain = "deploy"; break; + case "audit": domain = "audit"; break; + case "security": domain = "security"; break; + case "archive": domain = "archive"; break; + case "init": domain = "init"; break; + case "learn": domain = "learn"; break; + case "legal": domain = "legal"; break; + case "privacy": domain = "privacy"; break; + case "trust": domain = "trust"; break; + case "complete": domain = "complete"; break; + case "progress": domain = "progress"; break; + default: domain = "governance"; + } + String[][] all = {{"䷀","Qian","governance"},{"䷁","Kun","archive"},{"䷂","Zhun","init"}, + {"䷃","Meng","learn"},{"䷄","Xu","async"},{"䷅","Song","legal"}, + {"䷜","Kan","engine"},{"䷝","Li","audit"},{"䷲","Zhen","security"}, + {"䷳","Gen","privacy"},{"䷸","Xun","deploy"},{"䷹","Dui","trust"}, + {"䷾","JiJi","complete"},{"䷿","WeiJi","progress"}}; + for (String[] h : all) if (h[2].equals(domain)) return new String[]{h[0], h[1]}; + return new String[]{"䷀","Qian"}; + } +} diff --git a/adapters/java/src/main/java/cn/uid9622/longhun/LongHunAdapter.java b/adapters/java/src/main/java/cn/uid9622/longhun/LongHunAdapter.java new file mode 100644 index 0000000..11e93e8 --- /dev/null +++ b/adapters/java/src/main/java/cn/uid9622/longhun/LongHunAdapter.java @@ -0,0 +1,51 @@ +package cn.uid9622.longhun; + +import java.time.ZonedDateTime; +import java.time.ZoneOffset; +import java.util.*; + +public class LongHunAdapter { + private final String uid, device, locale; + private final DNAGenerator dnaGen; + private final AuditWrapper audit; + private final Validator validator; + + public LongHunAdapter(String uid, String device, String locale) { + this.uid = uid != null ? uid : "9622"; + this.device = device != null ? device : "HM-9622-001"; + this.locale = locale != null ? locale : "Asia/Shanghai"; + this.dnaGen = new DNAGenerator(this.uid, this.device, this.locale); + this.audit = new AuditWrapper(this.uid); + this.validator = new Validator(); + } + + public Map wrap(Object data, String taskType, String persona, String action, String version) { + if (taskType == null || taskType.isEmpty()) taskType = "default"; + if (persona == null || persona.isEmpty()) persona = "P04"; + if (action == null || action.isEmpty()) action = "WRAP"; + Map result = new LinkedHashMap<>(); + result.put("dna", dnaGen.generate(taskType, action, version)); + result.put("audit", audit.wrap(data, taskType, persona)); + result.put("payload", data); + Map meta = new LinkedHashMap<>(); + meta.put("adapter_version","1.0.0"); + meta.put("uid",uid); + meta.put("device",device); + meta.put("task_type",taskType); + meta.put("persona",persona); + meta.put("generated_at", ZonedDateTime.now(ZoneOffset.UTC).toString()); + meta.put("format","longhun-v∞"); + result.put("meta", meta); + return result; + } + + public ValidationResult validate(Map wrapped) { return validator.validate(wrapped); } + + public Map getSchemas() { + Map schemas = new LinkedHashMap<>(); + schemas.put("dnaSchema", Map.of("type","string","description","v∞ DNA traceability code")); + schemas.put("auditSchema", Map.of("type","object","required", + List.of("audit_version","uid","behavior_signature","behavior_pattern","behavior_labels","color"))); + return schemas; + } +} diff --git a/adapters/java/src/main/java/cn/uid9622/longhun/ValidationResult.java b/adapters/java/src/main/java/cn/uid9622/longhun/ValidationResult.java new file mode 100644 index 0000000..27bcf6b --- /dev/null +++ b/adapters/java/src/main/java/cn/uid9622/longhun/ValidationResult.java @@ -0,0 +1,17 @@ +package cn.uid9622.longhun; + +import java.util.List; + +public class ValidationResult { + private final boolean valid; + private final List errors, warnings; + private final String summary; + + public ValidationResult(boolean valid, List errors, List warnings, String summary) { + this.valid = valid; this.errors = errors; this.warnings = warnings; this.summary = summary; + } + public boolean isValid() { return valid; } + public List getErrors() { return errors; } + public List getWarnings() { return warnings; } + public String getSummary() { return summary; } +} diff --git a/adapters/java/src/main/java/cn/uid9622/longhun/Validator.java b/adapters/java/src/main/java/cn/uid9622/longhun/Validator.java new file mode 100644 index 0000000..6a721a1 --- /dev/null +++ b/adapters/java/src/main/java/cn/uid9622/longhun/Validator.java @@ -0,0 +1,65 @@ +package cn.uid9622.longhun; + +import java.util.*; +import java.util.regex.Pattern; + +public class Validator { + private static final Pattern DNA_REGEX = Pattern.compile( + "^#LongHun⚡️([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([\\u4DC0-\\u4DFF][A-Za-z]+)-(.+)-([a-f0-9]{8})$"); + private static final Set REQUIRED_TOP = Set.of("dna","audit","payload","meta"); + private static final Set REQUIRED_AUDIT = Set.of("audit_version","uid","behavior_signature","behavior_pattern","behavior_labels","color"); + private static final Set REQUIRED_SIG = Set.of("P","F","T","E","C","R","A","X","Y","Z"); + + private List errors = new ArrayList<>(); + private List warnings = new ArrayList<>(); + + public ValidationResult validate(Map wrapped) { + errors.clear(); warnings.clear(); + if (wrapped == null || wrapped.isEmpty()) { errors.add("Input null/empty"); return result(); } + + for (String k : REQUIRED_TOP) if (!wrapped.containsKey(k)) errors.add("Missing top key: "+k); + + Object dnaObj = wrapped.get("dna"); + if (dnaObj == null || "".equals(dnaObj)) errors.add("DNA empty"); + else if (!DNA_REGEX.matcher((String)dnaObj).matches()) + errors.add("DNA regex fail: "+((String)dnaObj).substring(0, Math.min(60, ((String)dnaObj).length()))); + + Object auditObj = wrapped.get("audit"); + if (!(auditObj instanceof Map)) errors.add("Audit not object"); + else validateAudit((Map)auditObj); + + return result(); + } + + private void validateAudit(Map audit) { + for (String k : REQUIRED_AUDIT) if (!audit.containsKey(k)) errors.add("Missing audit key: "+k); + Object sigObj = audit.get("behavior_signature"); + if (!(sigObj instanceof Map)) errors.add("sig not object"); + else { + Map sig = (Map)sigObj; + for (String k : REQUIRED_SIG) if (!sig.containsKey(k)) errors.add("Missing sig key: "+k); + validateSigValues(sig); + } + } + + private void validateSigValues(Map sig) { + Set vp = Set.of("HasPromise","NoPromise"); + Set vf = Set.of("Fulfilled","Unfulfilled","Partial"); + Set ve = Set.of("Willing","Perfunctory","Resentful","Numb"); + Set va = Set.of("Self","Partner","Family","Outsider","Public"); + Set vx = Set.of("OverExplain","Silent","Genuine","Indifferent"); + Set vy = Set.of("Changed","Resisted","Indifferent","NoResponse"); + + if (!vp.contains(sig.get("P"))) warnings.add("Invalid P: "+sig.get("P")); + if (!vf.contains(sig.get("F"))) warnings.add("Invalid F: "+sig.get("F")); + if (!ve.contains(sig.get("E"))) warnings.add("Invalid E: "+sig.get("E")); + if (!va.contains(sig.get("A"))) warnings.add("Invalid A: "+sig.get("A")); + if (!vx.contains(sig.get("X"))) warnings.add("Invalid X: "+sig.get("X")); + if (!vy.contains(sig.get("Y"))) warnings.add("Invalid Y: "+sig.get("Y")); + } + + private ValidationResult result() { + boolean valid = errors.isEmpty(); + return new ValidationResult(valid, errors, warnings, valid ? "✅ VALID" : "❌ INVALID"); + } +}