java+数据库+D3.js 实时查询人物关系图
先看下 效果
某个用户,邀请了自己的朋友 ,自己的朋友邀请了其他朋友,1 展示邀请关系,2 点击头像显示邀请人和被邀请人的关系。(网上这种资料很少, 另外很多都是从JSON文件取 数据, 这里是从数据库取数据)
=================================================================================================需求:Java从数据库查用户表,管理平台可以实时查看每个月用户的邀请关系图。
以下是代码:
1.Java 方法
1.1 controller 根据某个手机号码查出JSON
/** * @param model * @param mobile * @return */ @RequestMapping(value = "/relation") public ModelAndView toRelation(Model model,@RequestParam("mobile")String mobile) { String userName= AuthUtils.getAuthenticationObject().getName(); Customer user =userService.getUserByname(userName); model.addAttribute("role", user.getRole()); model.addAttribute("username", user.getMobile()); JSONObject json = customerService.findUserRelation(mobile); model.addAttribute("json", json); System.out.println(json); return new ModelAndView("relation"); }
1.2 service 方法(递归查询,JSON处理)
/** * 根据手机号码查询人物关系图 * @param mobile * @return */ public JSONObject findUserRelation(String mobile) { JSONObject json = new JSONObject(); // 首先查询当前用户 Customer customer = userService.getUserByname(mobile); List<RelationBo> list = new ArrayList<RelationBo>(); // 根节点,只看子类 // 递归获取当前用户的所有子类。 list = getAllRelation(new ArrayList<RelationBo>(), customer.getUid()); list.add(0, new RelationBo(mobile,Constants.TOP_USER, customer.getUid(), null)); // 获取用户信息JSONArray JSONArray peopleArray = new JSONArray(); JSONArray targetArray = new JSONArray(); SourceTargetBo bo = null; JSONObject peoJ= null; List<String> photoList= RelationUtil.getRandomPhoto(); for (int i = 0; i < list.size(); i++) { peoJ=new JSONObject(); peoJ.put("name", list.get(i).getName()); // peoJ.put("image", list.get(i).getImage()); peoJ.put("image", photoList.get(i)); peopleArray.add(peoJ); for (int j = 0; j < list.size(); j++) { if(list.get(j).getPuid()==null||list.get(j).getPuid()==list.get(i).getUid()){ continue; } if (list.get(i).getUid().equals(list.get(j).getPuid())) { bo = new SourceTargetBo(i, j,"resolved",RelationUtil.getRelation()); // 找到子类序号 targetArray.add(bo); } } } // 人物JSON json.put("nodes", peopleArray); // 关系JSON json.put("edges", targetArray); return json; } private List<RelationBo> getAllRelation(List<RelationBo> bo, String uid) { // 查询这个用户下的所有子用户 List<Customer> list = customerDao.findAllChild(uid); RelationBo relation = null; if (list == null || list.size() == 0) { // 当前用户没有子用户了! return bo; } else { for (Customer cus : list) { relation = new RelationBo(cus.getMobile(), Constants.BOOTOM_USER, cus.getUid(), cus.getPuid()); // 所有子用户添加到树中 bo.add(relation); // 递归查询子用户的所有子用户 bo = getAllRelation(bo, cus.getUid()); } } return bo; }
1.3 mybatis
<select id="findAllChild" resultType="com.ycmedia.entity.Customer"> select * from r_user where puid=#{uid} </select>
1.4 数据库 因为数据库暂时没有用户头像,所以随机工具类定义了几个头像
1.5 工具类, 获得随机关系, 用户头像
package com.ycmedia.utils; import java.util.ArrayList; import java.util.List; import java.util.Random; public class RelationUtil { public static String getRelation(){ List<String> list = new ArrayList<String>(); list.add(""); list.add("亲人"); list.add("同学"); list.add("朋友"); list.add("同事"); list.add("邻居"); int Num=new Random().nextInt(4)+1; return list.get(Num); } public static List<String> getRandomPhoto(){ List<String> list = new ArrayList<String>(); list.add("http://apps.ycmedia.cn/qm/img/31f96dc747eb4e4383ab7f990afc9775.jpg"); list.add("http://appdemo.b0.upaiyun.com/qm/img/lable/60x60/1480730644618.jpg"); list.add("http://appdemo.b0.upaiyun.com/qm/img/lable/60x60/1480730825975.jpg"); list.add("http://appdemo.b0.upaiyun.com/qm/img/lable/60x60/1480730901129.jpg"); list.add("http://appdemo.b0.upaiyun.com/qm/img/lable/60x60/1480730947894.jpg"); return list; } }
1.7 最后的JSON格式
=========================以上是Java, 下面是 html
2.1 html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>邀请关系图</title> <style> .nodetext { font-size: 12px ; font-family: SimSun; fill:#000000; } .linetext { font-size: 12px ; font-family: SimSun; fill:#0000FF; fill-opacity:0.0; } path.link { fill: none; stroke: #666; stroke-width: 1.5px; } marker#licensing { fill: green; } path.link.licensing { stroke: green; } path.link.resolved { stroke: green; } circle { fill: #ccc; stroke: #333; stroke-width: 1.5px; } text { font: 10px sans-serif; pointer-events: none; } text.shadow { stroke: #fff; stroke-width: 3px; stroke-opacity: .8; } </style> </head> <body> <input type="hidden" th:value="${json}" id="json" /> <script src="/bootstrap/jQuery/jquery-2.2.3.min.js"></script> <script src="/js/d3.v3.min.js" charset="utf-8"></script> <script> var obj=$("#json").val(); var root = JSON.parse(obj); var width = 1200; var height = 1200; var img_w = 77; var img_h = 90; var svg = d3.select("body").append("svg:svg") .attr("width", width) .attr("height", height); var force = d3.layout.force() .nodes(root.nodes) .links(root.edges) .size([width,height]) .linkDistance(200) .charge(-1500) .start(); //控制线条 var edges_line = svg.selectAll("line") .data(root.edges) .enter() .append("line") .style("stroke","#ccc") .style("stroke-width",1); //控制文字 var edges_text = svg.selectAll(".linetext") .data(root.edges) .enter() .append("text") .attr("class","linetext") .text(function(d){ return d.relation; }); var path = svg.append("svg:g").selectAll("path") .data(force.links()) .enter().append("svg:path") .attr("class", function(d) { return "link " + d.type; }) .attr("marker-end", function(d) { return "url(#" + d.type + ")"; }); //控制图片 var nodes_img = svg.selectAll("image") .data(root.nodes) .enter() .append("image") .attr("width",img_w) .attr("height",img_h) .attr("xlink:href",function(d){ return d.image; }) .on("mouseover",function(d,i){ edges_text.style("fill-opacity",function(edge){ if( edge.source === d || edge.target === d ){ return 1.0; } }); }) .on("mouseout",function(d,i){ edges_text.style("fill-opacity",function(edge){ if( edge.source === d || edge.target === d ){ return 0.0; } }); }) .call(force.drag); var text_dx = -20; var text_dy = 20; var nodes_text = svg.selectAll(".nodetext") .data(root.nodes) .enter() .append("text") .attr("class","nodetext") .attr("dx",text_dx) .attr("dy",text_dy) .text(function(d){ return d.name; }); force.on("tick", function(){ edges_text.attr("x",function(d){ return (d.source.x + d.target.x) / 2 ; }); edges_text.attr("y",function(d){ return (d.source.y + d.target.y) / 2 ; }); nodes_img.attr("x",function(d){ return d.x - img_w/2; }); nodes_img.attr("y",function(d){ return d.y - img_h/2; }); nodes_text.attr("x",function(d){ return d.x }); nodes_text.attr("y",function(d){ return d.y + img_w/2; }); path.attr("d", function(d) { var dx = d.target.x - d.source.x,//增量 dy = d.target.y - d.source.y, dr = Math.sqrt(dx * dx + dy * dy); return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y; }); }); </script> </body> </html>
以上就是全部代码, 目前未实现箭头,昨天搞了好长时间, 没弄出来, Java的画图工具不行, 太丑, 现在D3Js 很火,这里做个参考