kFeedback的更多介绍,请详见:http://my.oschina.net/kzhou/blog/98855
为什么开发kFeedback?
我像很多人一样,平时在闲暇之余做一些app,本人的作品主要有:
今后还会有更多的app发布,我在想,好的app离不开用户的反馈,如果每一个app内嵌一个反馈(又叫留言)的话,反馈信息会分散,不利于作者进行统一的受理,所以才有了今天的kFeedback。kFeedback发布几天来,目前已有60人在试用,而我现在唯一担心的是,CloudFoundry能不能顶住?
废话不多说了,接下来我将kFeedback源码发出,感兴趣的朋友可以参考,由于系统比较简单,我想下边的源码可能也就会对初学者有点帮助,老手们不要“耻笑”。
kFeedback用了哪些技术?
jsp + html5 + BootStrap + jQuery + servlet + spring(jdbcTemplate) + mySQL
源码:
工程目录结构:
Action.java - 一个DispatchServlet
Demo.java - 一个基于HttpClient的测试类
Service.java - 封装了所有业务的Dao
index.jsp - 首页
main.jsp - 主页面
其它的没啥了,都是一些基本(标准)的配置文件。
我们从Service.java说起:
code:
package k.feedback; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.apache.commons.mail.Email; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.SimpleEmail; import org.springframework.jdbc.core.JdbcTemplate; import sun.misc.BASE64Encoder; import com.alibaba.fastjson.JSON; public class Service { final static int PAGE_QTY = 15;//每页展示的记录数 private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } //初始化数据库表结构 public void initDB(){ //drop table this.jdbcTemplate.execute("drop table IF EXISTS feedback"); this.jdbcTemplate.execute("drop table IF EXISTS app"); this.jdbcTemplate.execute("drop table IF EXISTS emp"); System.out.println("table drop ok"); StringBuilder emp = new StringBuilder(); emp.append(" CREATE TABLE emp( "); emp.append(" emp_id INT AUTO_INCREMENT, "); emp.append(" emp_email VARCHAR(128), "); emp.append(" emp_login_pwd VARCHAR(40), "); emp.append(" cdate TIMESTAMP, "); emp.append(" PRIMARY KEY (emp_id) "); emp.append(" )ENGINE=MYISAM "); StringBuilder app = new StringBuilder(); app.append(" CREATE TABLE app( "); app.append(" app_id INT AUTO_INCREMENT, "); app.append(" emp_id INT, "); app.append(" app_name VARCHAR(255), "); app.append(" app_desc VARCHAR(255), "); app.append(" app_token VARCHAR(40), "); app.append(" app_feedback_qty INT, "); app.append(" cdate TIMESTAMP, "); app.append(" PRIMARY KEY (app_id) "); app.append(" )ENGINE=MYISAM "); StringBuilder feedback = new StringBuilder(); feedback.append(" CREATE TABLE feedback( "); feedback.append(" feedback_id INT AUTO_INCREMENT, "); feedback.append(" emp_id INT, "); feedback.append(" app_id INT, "); feedback.append(" feedback_time TIMESTAMP, "); feedback.append(" feedback_info TEXT, "); feedback.append(" PRIMARY KEY (feedback_id) "); feedback.append(" )ENGINE=MYISAM "); String alter1 = "ALTER TABLE app ADD CONSTRAINT Refemp1 FOREIGN KEY (emp_id) REFERENCES emp(emp_id)"; String alter2 = "ALTER TABLE feedback ADD CONSTRAINT Refemp2 FOREIGN KEY (emp_id) REFERENCES emp(emp_id)"; String alter3 = "ALTER TABLE feedback ADD CONSTRAINT Refapp3 FOREIGN KEY (app_id) REFERENCES app(app_id)"; String createIndex4Emp = "create unique index idx_email_pwd on emp(emp_email,emp_login_pwd)"; String createIndex4App = "create unique index idx_token on app(app_token)"; this.jdbcTemplate.execute(emp.toString()); System.out.println("emp table created"); this.jdbcTemplate.execute(app.toString()); System.out.println("app table created"); this.jdbcTemplate.execute(feedback.toString()); System.out.println("feedback table created"); this.jdbcTemplate.execute(alter1); this.jdbcTemplate.execute(alter2); this.jdbcTemplate.execute(alter3); System.out.println("alter ok"); this.jdbcTemplate.execute(createIndex4Emp); this.jdbcTemplate.execute(createIndex4App); //创建默认账户 this.jdbcTemplate.update("INSERT INTO emp(emp_id,emp_email, emp_login_pwd ) values(1,?,?)","[email protected]",encodeByMd5("111111")); System.out.println("kFeedback default user create ok"); //创建默认app this.jdbcTemplate.update("INSERT INTO app(app_id, emp_id, app_name, app_desc, app_token, app_feedback_qty) VALUES (1, 1, 'kFeedback-云反馈', '把你的产品反馈信息放在云上进行统一管理', 'kFeedback', 0)"); System.out.println("kFeedback default app create ok"); System.out.println("kFeedback db create ok"); } //开发者注册 public int reg(String email,String pwd){ //check email is exist int rv = this.jdbcTemplate.queryForInt("select count(*) from emp where emp_email = ?",email); if(rv == 0){ final String sql = "INSERT INTO emp(emp_email, emp_login_pwd ) values(?,?)"; return this.jdbcTemplate.update(sql,email,encodeByMd5(pwd)); }else{ return -999; } } //开发者修改密码 public void updPwd(String empId,String newPwd){ final String sql = "update emp set emp_login_pwd = ? where emp_id = ?"; this.jdbcTemplate.update(sql,encodeByMd5(newPwd),empId); } //登录 public String login(String email,String pwd){ final String sql = "select convert(emp_id,char) as emp_id,emp_email from emp where emp_email = ? and emp_login_pwd = ?"; Map<String,Object> map = new HashMap<String,Object>(); try { map = this.jdbcTemplate.queryForMap(sql,email,encodeByMd5(pwd)); map.put("result", "1"); return JSON.toJSONString(map); } catch (Exception e) { map.put("result", "0"); return JSON.toJSONString(map); } } //忘记密码 public int forgetPwd(String email){ int rv = this.jdbcTemplate.queryForInt("select count(*) from emp where emp_email = ?",email); if(rv > 0){ //get new pwd String newPwd = this.getAppToken(); //upd emp pwd this.jdbcTemplate.update("update emp set emp_login_pwd = ? where emp_email = ?",encodeByMd5(newPwd),email); //send new pwd to email box StringBuilder msg = new StringBuilder(); msg.append("您好,您的kFeedback新密码为:").append(newPwd).append("\n\n").append("请重新登录;http://kfeedback.cloudfoundry.com"); this.sendEmail("来自kFeedback的密码重置邮件", email, msg.toString()); return 1;//成功 }else{ return 0; } } //新增产品-strings[0],strings[1],strings[2]=emp_id,app_name,app_desc public void insApp(String...strings){ final String sql = "INSERT INTO app (emp_id, app_name, app_desc,app_feedback_qty, app_token) values(?,?,?,0,?)"; this.jdbcTemplate.update(sql,strings[0],strings[1],strings[2],getAppToken()); } //删除产品 public void delApp(String appId){ this.jdbcTemplate.update("delete from feedback where app_id = ?",appId); this.jdbcTemplate.update("delete from app where app_id = ?",appId); } //修改产品-strings[0],strings[1],strings[2]=app_name,app_desc,app_id public void updApp(String...strings){ final String sql = "update app set app_name=?,app_desc=? where app_id = ?"; this.jdbcTemplate.update(sql,strings[0],strings[1],strings[2]); } //查询产品 public String getApp(String empId){ if(empId.equals("1")){ return JSON.toJSONString(this.jdbcTemplate.queryForList("select convert(app_id,char) as app_id,convert(emp_id,char) as emp_id,app_name,app_desc,app_token,convert(app_feedback_qty,char) as app_feedback_qty,convert(DATE_FORMAT(cdate,'%Y-%m-%d'),char) as cdate from app where emp_id = ?",empId)); }else{ return JSON.toJSONString(this.jdbcTemplate.queryForList("select convert(app_id,char) as app_id,convert(emp_id,char) as emp_id,app_name,app_desc,app_token,convert(app_feedback_qty,char) as app_feedback_qty,convert(DATE_FORMAT(cdate,'%Y-%m-%d'),char) as cdate from app where app_id = 1 union all select convert(app_id,char) as app_id,convert(emp_id,char) as emp_id,app_name,app_desc,app_token,convert(app_feedback_qty,char) as app_feedback_qty,convert(DATE_FORMAT(cdate,'%Y-%m-%d'),char) as cdate from app where emp_id = ?",empId)); } } //根据app_token反查app_id private Map<String,Object> getAppIdByToken(String token){ return this.jdbcTemplate.queryForMap("select convert(a.emp_id,char) as emp_id,convert(a.app_id,char) as app_id,e.emp_email,a.app_name from app a,emp e where a.emp_id = e.emp_id and a.app_token = ?",token); } //根据产品查询反馈信息 public String getFeedback(String appId){ final String sql = "select convert(feedback_id,char) as feedback_id,convert(DATE_FORMAT(feedback_time,'%Y-%m-%d %H:%i:%s'),char) as feedback_time,feedback_info from feedback where app_id = ? order by feedback_time"; return JSON.toJSONString(this.jdbcTemplate.queryForList(sql,appId)); } //删除反馈信息 public void delFeedback(String feedbackId){ this.jdbcTemplate.update("delete from feedback where feedback_id = ?",feedbackId); } //开放api //新增反馈-strings[0],strings[1]]=app_token,feedback_info public void insFeedback(String...strings){ Map<String,Object> map = getAppIdByToken(strings[0]); String appId = map.get("app_id").toString(); final String sql = "INSERT INTO feedback (emp_id, app_id, feedback_info) values(?,?,?)"; this.jdbcTemplate.update(sql,map.get("emp_id"),appId,strings[1]); int feedbackQty = this.jdbcTemplate.queryForInt("select count(*) from feedback where app_id = ?",appId); this.jdbcTemplate.update("update app set app_feedback_qty=? where app_id = ?",feedbackQty,appId); //send email to app creator // StringBuilder msgTitle = new StringBuilder(); // msgTitle.append("[来自kFeedback]:您的<").append(map.get("app_name").toString()).append(">App收到了新的反馈信息"); // StringBuilder msg = new StringBuilder(); // msg.append("[来自kFeedback]:您的<").append(map.get("app_name").toString()).append(">App收到了新的反馈信息").append("\n\n"); // msg.append("新的反馈信息为:\n\n"); // msg.append("TA说: ").append(strings[1]).append("\n\n").append("请访问;http://kfeedback.cloudfoundry.com").append("\n\n"); // this.sendEmail(msgTitle.toString(), map.get("emp_email").toString(), msg.toString()); } //开发api end //MD5 private String encodeByMd5(String str) { MessageDigest md5; try { md5 = MessageDigest.getInstance("MD5"); BASE64Encoder base64en = new BASE64Encoder(); return base64en.encode(md5.digest(str.getBytes("utf-8"))); } catch (Exception e) { return str; } } //UUID public String getAppToken(){ return System.currentTimeMillis()+""; } /** * 发送电子邮件 * @param title * @param emailAddr * @param msg * @throws EmailException */ public void sendEmail(String title,String emailAddr,String msg){ try { Email email = new SimpleEmail(); email.setHostName("smtp.163.com"); email.setAuthentication("username", "password"); email.setFrom("[email protected]"); email.setSubject(title); email.setCharset("utf8"); email.setMsg(msg); email.addTo(emailAddr); email.send(); } catch (Exception e) { } } }
Action.java
code:
package k.feedback; import java.io.IOException; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; /** * Servlet implementation class Action */ public class Action extends HttpServlet { private static final long serialVersionUID = 1L; private WebApplicationContext ctx = null; /** * @see HttpServlet#HttpServlet() */ public Action() { super(); } /** * @see Servlet#init(ServletConfig) */ public void init(ServletConfig config) throws ServletException { ctx = WebApplicationContextUtils.getWebApplicationContext(config.getServletContext()); } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // request.setCharacterEncoding("utf-8"); // response.setCharacterEncoding("utf-8"); response.setHeader("Cache-Control", "no-cache"); response.setContentType("text/javascript"); Service service = (Service)ctx.getBean("service"); String callback = request.getParameter("callback"); String method = request.getParameter("m"); System.out.println("request method : " + method); if(method == null || method.trim().length() == 0){ response.getWriter().print("您的请求是非法的!"); }else{ if(method.equals("login")){ this.login(service,request,response,callback); }else if(method.equals("reg")){ this.reg(service,request,response,callback); }else if(method.equals("updPwd")){ this.updPwd(service,request,response,callback); }else if(method.equals("insApp")){ this.insApp(service,request,response,callback); }else if(method.equals("getApp")){ this.getApp(service,request,response,callback); }else if(method.equals("getFeedback")){ this.getFeedback(service,request,response,callback); }else if(method.equals("updApp")){ this.updApp(service,request,response,callback); }else if(method.equals("delApp")){ this.delApp(service,request,response,callback); }else if(method.equals("insFeedback")){ this.insFeedback(service,request,response,callback); } } } private void insFeedback(Service service, HttpServletRequest request,HttpServletResponse response, String callback) throws IOException { String token = request.getParameter("token"); String fb = request.getParameter("fb"); service.insFeedback(token,fb); response.getWriter().write(callback+"({\"result\" : \"1\"})"); } private void delApp(Service service, HttpServletRequest request,HttpServletResponse response, String callback) throws IOException { String appId =request.getParameter("appId"); service.delApp(appId); response.getWriter().write(callback+"({\"result\" : \"1\"})"); } private void updApp(Service service, HttpServletRequest request,HttpServletResponse response, String callback) throws IOException { String appId = request.getParameter("appId"); String appName = request.getParameter("appName"); String appDesc = request.getParameter("appDesc"); service.updApp(appName,appDesc,appId); response.getWriter().write(callback+"({\"result\" : \"1\"})"); } private void getFeedback(Service service, HttpServletRequest request,HttpServletResponse response, String callback) throws IOException { String appId = request.getParameter("appId"); String feedbackJson = service.getFeedback(appId); response.getWriter().write(callback+"("+feedbackJson+")"); } private void getApp(Service service, HttpServletRequest request,HttpServletResponse response, String callback) throws IOException { String empId = request.getParameter("empId"); String appJson = service.getApp(empId); response.getWriter().write(callback+"("+appJson+")"); } private void insApp(Service service, HttpServletRequest request,HttpServletResponse response, String callback) throws IOException { String empId = request.getParameter("empId"); String appName = request.getParameter("appName"); String appDesc = request.getParameter("appDesc"); service.insApp(empId,appName,appDesc); response.getWriter().write(callback+"({\"result\" : \"1\"})"); } private void updPwd(Service service, HttpServletRequest request,HttpServletResponse response, String callback) throws IOException { String empId = request.getParameter("empId"); String newPwd = request.getParameter("newPwd"); service.updPwd(empId, newPwd); response.getWriter().write(callback+"({\"result\" : \"1\"})"); } private void reg(Service service, HttpServletRequest request,HttpServletResponse response, String callback) throws IOException { String reg_email = request.getParameter("reg_email"); String reg_pwd = request.getParameter("reg_pwd"); int rv = service.reg(reg_email, reg_pwd); String output = callback+"({})"; if(rv == 1){ output = callback+"({\"result\" : \"1\"})";//reg成功 }else{ output = callback+"({\"result\" : \"0\"})";//reg失败 } response.getWriter().write(output); } private void login(Service service, HttpServletRequest request,HttpServletResponse response, String callback) throws IOException, ServletException { String email = request.getParameter("email"); String pwd = request.getParameter("pwd"); String loginInfo = service.login(email, pwd); response.getWriter().write(callback+"("+loginInfo+")"); } }index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>kFeedback - 云反馈!</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="feedback system on Cloud Foundry"> <meta name="author" content="zhoukai"> <!-- Le styles --> <link href="css/bootstrap.css" rel="stylesheet"> <style type="text/css"> body { padding-top: 15px; } </style> <link href="css/bootstrap-responsive.css" rel="stylesheet"> <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> </head> <body> <div class="container"> <!-- Main hero unit for a primary marketing message or call to action --> <div class="hero-unit"> <h1>kFeedback - 云反馈!</h1> <p>kFeedback是一个极简主义作品。</p> <p>成功的产品离不开用户的反馈;kFeedback就是你的产品与最终用户之间的沟通桥梁。</p> </div> <!-- Example row of columns --> <div class="row"> <div class="span4"> <blockquote> <p><strong>kFeedback基本路径(Guide).</strong></p> <small>kFeedback使用指南</small> </blockquote> <p> <ol> <li>注册kFeedback账号(Reg account)</li> <li>登录kFeedback(Login)</li> <li>创建App(New app)</li> <li>将系统生成的API嵌入到你的产品中(Embed kFeedback post-api in your projduct)</li> <li>OK</li> </ol> </p> </div> <div class="span4"> <blockquote> <p><strong>登录kFeedback系统(Login).</strong></p> <small>用您注册的电子邮件和密码登录系统</small> </blockquote> <p> <label class="control-label" for="login_email">电子邮件(Email)</label> <input type="text" id="login_email" placeholder="Email"> <label class="control-label" for="login_pwd">登录密码(Password)</label> <input type="password" id="login_pwd" placeholder="Password"> <p> <button id="btn_login" class="btn btn-primary">登录(Login now!)</button> </p> </p> </div> <div class="span4"> <blockquote> <p><strong>注册kFeedback账号(Register).</strong></p> <small>提供您的电子邮件及登录密码,瞬间即可完成注册</small> </blockquote> <p> <label class="control-label" for="reg_email">电子邮件</label> <input type="text" id="reg_email" placeholder="Login email"> <label class="control-label" for="reg_pwd">登录密码</label> <input type="password" id="reg_pwd" placeholder="Login pwd"> <label class="control-label" for="reg_repwd">确认密码</label> <input type="password" id="reg_repwd" placeholder="Confirm pwd"> <p><button id="btn_reg" class="btn btn-primary">注册(Register now!)</button></p> </p> </div> </div> <hr> <footer> <div class="row"> <div class="span4">© <i class="icon-user"></i>恺哥作品 - <a href="mailto:[email protected]?subject=to 恺哥">[email protected]</a></div> <div class="span4"></div> <div class="span4"> <a target="_blank" href="http://www.oschina.net" title="OSChina"><img src="img/osc.ico" alt="OSChina"></a> <a target="_blank" href="http://www.cloudfoundry.com/" title="CloudFoundry"><img src="img/cf.ico" alt="Cloudfoundry"></a> <a target="_blank" href="http://twitter.github.com/bootstrap/" title="Bootstrap"><img src="img/bootstrap.ico" alt="Bootstrap"></a> </div> </div> </footer> <!-- dialog --> <div id="myAlertModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 id="myModalLabel">kFeedback信息提示窗</h3> </div> <div class="modal-body"> <div id="alertInfo"></div> </div> <div class="modal-footer"> <button class="btn" data-dismiss="modal">Close</button> </div> </div> </div> <!-- /container --> <!-- Le javascript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="js/jquery-1.8.3.js"></script> <script src="js/bootstrap.js"></script> <script type="text/javascript"> var app = "http://kfeedback.cloudfoundry.com"; var url = app + "/Action?callback=?"; $(document).ready(function() { //login email focus var localStorageLoginInfo = localStorage.getItem("login_email"); if(!$.isEmptyObject(localStorageLoginInfo)){ $("#login_email").val(localStorageLoginInfo); $("#login_pwd").focus(); }else{ $("#login_email").focus(); } $("#btn_reg").click(function(){ var reg_email = $("#reg_email").val(); var reg_pwd = $("#reg_pwd").val(); var reg_repwd = $("#reg_repwd").val(); var alertInfo = checkRegForm(reg_email,reg_pwd,reg_repwd); if($.isEmptyObject(alertInfo)){ $.getJSON(url,{m:"reg",reg_email:reg_email,reg_pwd:reg_pwd},function(json){ if(json.result == "1"){ //init login form $("#login_email").val(reg_email); $("#login_pwd").val(reg_pwd); $("#alertInfo").html("<p class=\"text-success\">账号注册成功,请登录kFeedback!</p>"); $('#myAlertModal').modal('show'); }else{ $("#alertInfo").html("<p class=\"text-error\">账号注册失败,您提供的电子邮件已被使用,请重新尝试!</p>"); $('#myAlertModal').modal('show'); } }); }else{ $("#alertInfo").html(alertInfo); $('#myAlertModal').modal('show'); } }); $("#login_pwd").keydown(function(e){ if(e.keyCode==13){ //回车 $("#btn_login").click(); } }); $("#btn_login").click(function(){ var login_email = $("#login_email").val(); var login_pwd = $("#login_pwd").val(); if($.isEmptyObject(login_email) || $.isEmptyObject(login_pwd)){ $("#alertInfo").html("<p class=\"text-error\">登录时,电子邮件和登录密码是必须的.</p>"); $('#myAlertModal').modal('show'); }else{ $.getJSON(url,{m:"login",email:login_email,pwd:login_pwd},function(json){ if(json.result == "0"){ $("#alertInfo").html("<p class=\"text-error\">登录失败,用户名或密码错误,请重新尝试!</p>"); $('#myAlertModal').modal('show'); }else{ sessionStorage.setItem("email", login_email); sessionStorage.setItem("empId", json.emp_id); localStorage.setItem("login_email",login_email); location.href=app+"/main.jsp"; } }); } }); }); //check reg form function checkRegForm(reg_email,reg_pwd,reg_repwd){ var alertInfo = ""; //check reg_email if($.isEmptyObject(reg_email)){ alertInfo += "<p class=\"text-error\">注册时,电子邮件不能为空.</p>"; } if(!verifyEmail(reg_email)){ alertInfo += "<p class=\"text-error\">您提供的电子邮件格式不合法.</p>"; } if($.isEmptyObject(reg_pwd)){ alertInfo += "<p class=\"text-error\">注册时,登录密码不能为空.</p>"; } if(reg_pwd != reg_repwd){ alertInfo += "<p class=\"text-error\">登录密码不一致,请重新检查.</p>"; } return alertInfo; } //email checker function verifyEmail(emailStr){ var emailRegEx = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i; if(emailStr.search(emailRegEx) == -1) { return false; }else{ return true; } } </script> </body> </html>main.html
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>kFeedback - 云反馈!</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="feedback system on Cloud Foundry"> <meta name="author" content="zhoukai"> <!-- Le styles --> <link href="css/bootstrap.css" rel="stylesheet"> <style type="text/css"> body { padding-top: 60px; padding-bottom: 5px; } </style> <link href="css/bootstrap-responsive.css" rel="stylesheet"> <link href="css/prettify.css" rel="stylesheet"> <script src="js/prettify.js"></script> <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> </head> <body onload="prettyPrint()"> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="navbar-inner"> <div class="container"> <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </a> <a class="brand" href="#">kFeedback - 云反馈!</a> <div class="nav-collapse collapse"> <ul class="nav"> <li><a href="#" id="goto_index">首页(Home)</a></li> <li><a href="#" id="goto_about">关于(About)</a></li> <li><a href="#" id="goto_demo">示例(Demo)</a></li> <li><a href="#" id="goto_updpwd">修改密码(Upd pwd)</a></li> <li class="active"><a href="#"><i class="icon-user icon-white"></i><span id="login_email"></span></a></li> </ul> </div><!--/.nav-collapse --> </div> </div> </div> <div class="container"> <h2>App反馈监控台(App feedback console)</h2> <hr> <div class="tabbable tabs-left"> <ul id="appList" class="nav nav-tabs"> <li class="active"><a href="#addApp" data-toggle="tab"><i class="icon-plus"></i>新增App(New App)</a></li> </ul> <div id="appList_content" class="tab-content"> <!-- 固定存在 --> <div class="tab-pane active" id="addApp"> <p> <div class="control-group"> <label class="control-label" for="app_name">App名称(<=100个字.)</label> <div class="controls"> <input class="input-xlarge" type="text" id="app_name" maxlength="100" placeholder="App name"> </div> </div> <div class="control-group"> <label class="control-label" for="app_desc">App描述(<=150个字.)</label> <div class="controls"> <textarea maxlength="150" class="input-xlarge" id="app_desc" rows="3"></textarea> </div> </div> <div class="control-group"> <div class="controls"> <button id="btn_addApp" class="btn btn-primary">新增App(New app now!)</button> </div> </div> </p> </div> <!-- 固定存在 end--> </div> </div> <hr> <footer> <div class="row"> <div class="span4">© <i class="icon-user"></i>恺哥作品 - <a href="mailto:[email protected]?subject=to 恺哥">[email protected]</a></div> <div class="span4"></div> <div class="span4"> <a target="_blank" href="http://www.oschina.net" title="OSChina"><img src="img/osc.ico" alt="OSChina"></a> <a target="_blank" href="http://www.cloudfoundry.com/" title="CloudFoundry"><img src="img/cf.ico" alt="cloudfoundry"></a> <a target="_blank" href="http://twitter.github.com/bootstrap/" title="Bootstrap"><img src="img/bootstrap.ico" alt="Bootstrap"></a> </div> </div> </footer> <!-- dialog --> <div id="updPwdModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 id="myModalLabel">kFeedback信息提示窗</h3> </div> <div class="modal-body"> <h4><span id="loginer"></span>修改密码</h4> <div id="updPwd_error_info"></div> <p> <label class="control-label" for="upd_new_pwd">新密码</label> <input type="password" id="upd_new_pwd" placeholder="New pwd"> <label class="control-label" for="upd_new_repwd">确认新密码</label> <input type="password" id="upd_new_repwd" placeholder="Confirm new pwd"> </p> </div> <div class="modal-footer"> <button class="btn" data-dismiss="modal">Close</button> <button id="btn_updpwd" class="btn btn-primary">提交(Update now!)</button> </div> </div> <div id="updAppModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 id="myModalLabel">kFeedback信息提示窗</h3> </div> <div class="modal-body"> <h4>修改App信息</h4> <div id="updApp_error_info"></div> <p> <label class="control-label" for="upd_app_token">App Token</label> <input type="text" disabled="disabled" class="input-xlarge uneditable-input" id="upd_app_token" placeholder=""> <label class="control-label" for="upd_app_name">App名称(<=100个字.)</label> <input type="text" maxlength="100" class="input-xlarge" id="upd_app_name" placeholder=""> <input type="hidden" id="upd_app_id" > <label class="control-label" for="upd_app_desc">App描述(<=150个字.)</label> <textarea maxlength="150" class="input-xlarge" id="upd_app_desc" rows="3"></textarea> </p> </div> <div class="modal-footer"> <button class="btn" data-dismiss="modal">Close</button> <button id="btn_updApp" class="btn btn-primary">提交(Update now!)</button> </div> </div> <div id="myAlertModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 id="myModalLabel">kFeedback信息提示窗</h3> </div> <div class="modal-body"> <div id="alertInfo"></div> </div> <div class="modal-footer"> <button class="btn" data-dismiss="modal">Close</button> </div> </div> <div id="myAlertModal4Reload" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 id="myModalLabel">kFeedback信息提示窗</h3> </div> <div class="modal-body"> <div id="alertInfo4Reload"></div> </div> <div class="modal-footer"> <button class="btn" data-dismiss="modal">Close</button> </div> </div> <div id="delAppModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 id="myModalLabel">kFeedback信息提示窗</h3> </div> <div class="modal-body"> <h4>删除App - <span id="delAppName"></span></h4> <p>删除App的同时,也会删除该App的所有反馈信息,请注意.</p> <p>删除App点击[删除App]按钮,取消删除操作请点击[取消]按钮.</p> <input type="hidden" id="del_app_id" > </div> <div class="modal-footer"> <button class="btn" data-dismiss="modal">取消(Cancel)</button> <button id="btn_delApp" class="btn btn-primary">删除App(Delete app now!)</button> </div> </div> <div id="aboutModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 id="myModalLabel">关于kFeedback</h3> </div> <div class="modal-body"> <div class="thumbnail"> <img src="img/me.png" alt="恺哥"> <div class="caption"> <h4>周恺(<a href="http://weibo.com/u/2697324452" target="_blank">@新浪微博</a>)</h4> <p>联通系统集成有限公司黑龙江省分公司 信息系统开发部 技术经理.</p> </div> </div> </div> <div class="modal-footer"> <button class="btn" data-dismiss="modal">Close</button> </div> </div> <div id="demoModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 id="myModalLabel">kFeedback Demo(示例)</h3> </div> <div class="modal-body"> <h4>如何将Post-API嵌入到您的产品中</h4> <div class="caption"> <h5>基于Apache HttpClient(推荐)</h5> <p> <pre class="prettyprint "> public static void sendFeedback(String appToken,String fb){ DefaultHttpClient httpclient = new DefaultHttpClient(); StringBuilder url = new StringBuilder(); url.append("http://kfeedback.cloudfoundry.com/Action"); try { HttpPost httpost = new HttpPost(url.toString()); List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("m", "insFeedback")); params.add(new BasicNameValuePair("token", appToken)); params.add(new BasicNameValuePair("fb", fb)); httpost.setEntity(new UrlEncodedFormEntity(params,HTTP.UTF_8)); ResponseHandler<String> responseHandler = new BasicResponseHandler(); httpclient.execute(httpost, responseHandler); } catch (Exception e) { }finally{httpclient.getConnectionManager().shutdown();} } </pre> </p> </div> </div> <div class="modal-footer"> <button class="btn" data-dismiss="modal">Close</button> </div> </div> </div> <!-- /container --> <!-- Le javascript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="js/jquery-1.8.3.js"></script> <script src="js/bootstrap.js"></script> <script type="text/javascript"> $(document).ready(function(){ var app = "http://kfeedback.cloudfoundry.com"; var url = app + "/Action?callback=?"; //init login_info $("#login_email").html(sessionStorage.getItem("email")+"("+sessionStorage.getItem("empId")+")"); //init app tab $("#appList").empty();//清空列表 $("#appList").append("<li class=\"active\"><a href=\"#addApp\" data-toggle=\"tab\"><i class=\"icon-plus\"></i>新增App(New App)</a></li>"); $.getJSON(url,{m:"getApp",empId:sessionStorage.getItem("empId")},function(json){ $.each(json, function(i,item){ var appInfo = "<li><a id=\""+item.app_id+"\" href=\"#"+item.app_id+"t\" data-toggle=\"tab\" title=\""+item.app_desc+"\">"+item.app_name+" - <span class=\"badge badge-info\">"+item.app_feedback_qty+"</span></a></li>"; $("#appList").append(appInfo); $("#"+item.app_id).bind("click",function(){ if(item.app_id == 1){ if(sessionStorage.getItem("empId") == "1"){ //init app detail info var appTabContent = "<div class=\"tab-pane\" id=\""+item.app_id+"t\">"; appTabContent += " <p> <div class=\"row\"> "; if(item.app_feedback_qty > 0){ appTabContent += " <div class=\"span3\"><i class=\"icon-list\"></i><strong><a href=\"#\" id=\"loadAppFB"+item.app_id+"\">加载"+item.app_name+"的反馈信息</a></strong></div> "; }else{ appTabContent += " <div class=\"span3\"><i class=\"icon-list\"></i><strong>"+item.app_name+"无信息</strong></div> "; } appTabContent += " <div class=\"span3\"><i class=\"icon-edit\"></i><strong><a href=\"#\" id=\"go2updApp"+item.app_id+"\">修改"+item.app_name+"的信息</a></strong></div> "; appTabContent += " <div class=\"span3\"><i class=\"icon-remove\"></i><strong><a href=\"#\" id=\"go2delApp"+item.app_id+"\">删除"+item.app_name+"的所有信息</a></strong></div> "; appTabContent += " </div><hr> "; appTabContent += " <div class=\"row\"> "; appTabContent += " <div class=\"span2\"><p><strong>创建时间</strong></p><p>"+item.cdate+"</p></div> "; appTabContent += " <div class=\"span4\"><p><strong>描述</strong></p><p>"+item.app_desc+"</p></div> "; appTabContent += " <div class=\"span3\"><p><strong>Token</strong></p><p>"+item.app_token+"</p></div> "; appTabContent += " </div> </p> <hr> "; //init feedback info appTabContent += " <p> "; appTabContent += " <table class=\"table table-striped\"> "; appTabContent += " <thead> "; appTabContent += " <tr> "; appTabContent += " <th>#</th> "; appTabContent += " <th><i class=\"icon-time\"></i>反馈时间(Feedback time)</th> "; appTabContent += " <th><i class=\"icon-info-sign\"></i>反馈内容(Feedback content)</th> "; appTabContent += " <th><i class=\"icon-wrench\"></i>操作(Operate)</th> "; appTabContent += " </tr> "; appTabContent += " </thead> "; appTabContent += " <tbody id=\"fb_tbody"+item.app_id+"\"> "; appTabContent += " </tbody> "; appTabContent += " </table> "; appTabContent += " </p> "; appTabContent += "</div>"; $("#appList_content").append(appTabContent); }else{ //init app detail info var appTabContent = "<div class=\"tab-pane\" id=\""+item.app_id+"t\">"; appTabContent += "<div class=\"input-append\">"; appTabContent += " <input class=\"span6\" placeholder=\"留下您对kFeedback的感受\" id=\"feedback_info\" type=\"text\">"; appTabContent += " <button class=\"btn btn-info\" id=\"bth_addFeedback"+item.app_id+"\" type=\"button\">提交反馈</button>"; if(item.app_feedback_qty > 0){ appTabContent += " <button class=\"btn btn-primary\" id=\"btnLoadAppFB"+item.app_id+"\" type=\"button\">加载反馈信息</button>"; } appTabContent += "</div>"; appTabContent += " <hr> "; appTabContent += " <div class=\"row\"> "; appTabContent += " <div class=\"span2\"><p><strong>创建时间</strong></p><p>"+item.cdate+"</p></div> "; appTabContent += " <div class=\"span4\"><p><strong>描述</strong></p><p>"+item.app_desc+"</p></div> "; appTabContent += " <div class=\"span3\"><p><strong>Token</strong></p><p>"+item.app_token+"</p></div> "; appTabContent += " </div> </p> <hr> "; //init feedback info appTabContent += " <p> "; appTabContent += " <table class=\"table table-striped\"> "; appTabContent += " <thead> "; appTabContent += " <tr> "; appTabContent += " <th>#</th> "; appTabContent += " <th><i class=\"icon-time\"></i>反馈时间(Feedback time)</th> "; appTabContent += " <th><i class=\"icon-info-sign\"></i>反馈内容(Feedback content)</th> "; appTabContent += " <th><i class=\"icon-wrench\"></i>操作(Operate)</th> "; appTabContent += " </tr> "; appTabContent += " </thead> "; appTabContent += " <tbody id=\"fb_tbody"+item.app_id+"\"> "; appTabContent += " </tbody> "; appTabContent += " </table> "; appTabContent += " </p> "; appTabContent += "</div>"; $("#appList_content").append(appTabContent); } }else{ //init app detail info var appTabContent = "<div class=\"tab-pane\" id=\""+item.app_id+"t\">"; appTabContent += " <p> <div class=\"row\"> "; if(item.app_feedback_qty > 0){ appTabContent += " <div class=\"span3\"><i class=\"icon-list\"></i><strong><a href=\"#\" id=\"loadAppFB"+item.app_id+"\">加载"+item.app_name+"的反馈信息</a></strong></div> "; }else{ appTabContent += " <div class=\"span3\"><i class=\"icon-list\"></i><strong>"+item.app_name+"无信息</strong></div> "; } appTabContent += " <div class=\"span3\"><i class=\"icon-edit\"></i><strong><a href=\"#\" id=\"go2updApp"+item.app_id+"\">修改"+item.app_name+"的信息</a></strong></div> "; appTabContent += " <div class=\"span3\"><i class=\"icon-remove\"></i><strong><a href=\"#\" id=\"go2delApp"+item.app_id+"\">删除"+item.app_name+"的所有信息</a></strong></div> "; appTabContent += " </div><hr> "; appTabContent += " <div class=\"row\"> "; appTabContent += " <div class=\"span2\"><p><strong>创建时间</strong></p><p>"+item.cdate+"</p></div> "; appTabContent += " <div class=\"span4\"><p><strong>描述</strong></p><p>"+item.app_desc+"</p></div> "; appTabContent += " <div class=\"span3\"><p><strong>Token</strong></p><p>"+item.app_token+"</p></div> "; appTabContent += " </div> </p> <hr> "; //init feedback info appTabContent += " <p> "; appTabContent += " <table class=\"table table-striped\"> "; appTabContent += " <thead> "; appTabContent += " <tr> "; appTabContent += " <th>#</th> "; appTabContent += " <th><i class=\"icon-time\"></i>反馈时间(Feedback time)</th> "; appTabContent += " <th><i class=\"icon-info-sign\"></i>反馈内容(Feedback content)</th> "; appTabContent += " <th><i class=\"icon-wrench\"></i>操作(Operate)</th> "; appTabContent += " </tr> "; appTabContent += " </thead> "; appTabContent += " <tbody id=\"fb_tbody"+item.app_id+"\"> "; appTabContent += " </tbody> "; appTabContent += " </table> "; appTabContent += " </p> "; appTabContent += "</div>"; $("#appList_content").append(appTabContent); } $("#bth_addFeedback"+item.app_id).bind("click",function(){ var fbInfo = $("#feedback_info").val(); if($.isEmptyObject(fbInfo)){ $("#alertInfo").html("<p class=\"text-error\">提交反馈失败!反馈信息不能为空!</p>"); $('#myAlertModal').modal('show'); }else{ $.getJSON(url,{m:"insFeedback",token:"kFeedback",fb:fbInfo},function(json){ if(json.result == "1"){ $("#feedback_info").val(""); $("#alertInfo4Reload").html("<p class=\"text-success\">新增成功!感谢您对kFeedback的支持.</p>"); $('#myAlertModal4Reload').modal('show'); }else{ $("#alertInfo").html("<p class=\"text-error\">新增失败!</p>"); $('#myAlertModal').modal('show'); } }); } }); $("#btnLoadAppFB"+item.app_id).bind("click",function(){ $.getJSON(url,{m:"getFeedback",appId:item.app_id},function(json){ //clear tbody $("#fb_tbody"+item.app_id).empty(); var tbody = ""; if(json.length > 0){ $.each(json, function(j,feedbackItem){ tbody += "<tr>"; tbody += "<td>" + feedbackItem.feedback_id + "</td>"; tbody += "<td>" + feedbackItem.feedback_time + "</td>"; tbody += "<td>" + feedbackItem.feedback_info + "</td>"; tbody += "<td><i class=\"icon-comment\"></i><strong><a href=\"#\">预留</a></strong></td>"; tbody += "</tr>"; }); $("#fb_tbody"+item.app_id).append(tbody); } }); }); //bind load feedback info click event $("#loadAppFB"+item.app_id).bind("click",function(){ $.getJSON(url,{m:"getFeedback",appId:item.app_id},function(json){ //clear tbody $("#fb_tbody"+item.app_id).empty(); var tbody = ""; if(json.length > 0){ $.each(json, function(j,feedbackItem){ tbody += "<tr>"; tbody += "<td>" + feedbackItem.feedback_id + "</td>"; tbody += "<td>" + feedbackItem.feedback_time + "</td>"; tbody += "<td>" + feedbackItem.feedback_info + "</td>"; tbody += "<td><i class=\"icon-comment\"></i><strong><a href=\"#\">预留</a></strong></td>"; tbody += "</tr>"; }); $("#fb_tbody"+item.app_id).append(tbody); } }); }); //bind updApp and delApp click event $("#go2updApp"+item.app_id).bind("click",function(){ $("#upd_app_id").val(item.app_id); $("#upd_app_token").val(item.app_token); $("#upd_app_name").val(item.app_name); $("#upd_app_desc").val(item.app_desc); $('#updAppModal').modal('show'); }); $("#go2delApp"+item.app_id).bind("click",function(){ $("#del_app_id").val(item.app_id); $("#delAppName").html("<strong>"+item.app_name+"</strong>"); $('#delAppModal').modal('show'); }); }); }); }); //init app tab end $("#goto_index").click(function(){ //clear sessionStorage sessionStorage.clear(); //forward location.href = app + "/index.jsp"; }); $("#goto_about").click(function(){ $('#aboutModal').modal('show'); }); $("#goto_demo").click(function(){ $('#demoModal').modal('show'); }); $("#goto_updpwd").click(function(){ $("#updPwd_error_info").html(""); $("#upd_new_pwd").val(""); $("#upd_new_repwd").val(""); $("#loginer").html(sessionStorage.getItem("email")+"("+sessionStorage.getItem("empId")+")"); $('#updPwdModal').modal('show'); }); $("#btn_updpwd").click(function(){ var upd_new_pwd = $("#upd_new_pwd").val(); var upd_new_repwd = $("#upd_new_repwd").val(); if(!$.isEmptyObject(upd_new_pwd) && (upd_new_pwd == upd_new_repwd)){ $.getJSON(url,{m:"updPwd",empId:sessionStorage.getItem("empId"),newPwd:upd_new_pwd},function(json){ if(json.result == "1"){ $("#updPwd_error_info").html("<p class=\"text-success\">修改密码成功.</p>"); $("#upd_new_pwd").val(""); $("#upd_new_repwd").val(""); }else{ $("#updPwd_error_info").html("<p class=\"text-error\">修改密码失败.</p>"); } }); }else{ $("#updPwd_error_info").html("<p class=\"text-error\">修改密码失败,新密码为空或密码不一致,请重新尝试.</p>"); } }); $("#btn_addApp").click(function(){ var app_name = $("#app_name").val(); var app_desc = $("#app_desc").val(); if(!$.isEmptyObject(app_name) && !$.isEmptyObject(app_desc)){ $.getJSON(url,{m:"insApp",empId:sessionStorage.getItem("empId"),appName:app_name,appDesc:app_desc},function(json){ if(json.result == "1"){ $("#app_name").val(""); $("#app_desc").val(""); $("#alertInfo4Reload").html("<p class=\"text-success\">App新增成功!</p>"); $('#myAlertModal4Reload').modal('show'); }else{ $("#alertInfo").html("<p class=\"text-error\">App新增失败!</p>"); $('#myAlertModal').modal('show'); } }); }else{ $("#alertInfo").html("<p class=\"text-error\">App名称与描述为必填字段!</p>"); $('#myAlertModal').modal('show'); } }); $("#btn_updApp").click(function(){ var upd_app_id = $("#upd_app_id").val(); var upd_app_name = $("#upd_app_name").val(); var upd_app_desc = $("#upd_app_desc").val(); if(!$.isEmptyObject(upd_app_id) && !$.isEmptyObject(upd_app_name) && !$.isEmptyObject(upd_app_desc)){ $.getJSON(url,{m:"updApp",appId:upd_app_id,appName:upd_app_name,appDesc:upd_app_desc},function(json){ if(json.result == "1"){ $("#upd_app_id").val(""); $("#upd_app_token").val(""); $("#upd_app_name").val(""); $("#upd_app_desc").val(""); $('#updAppModal').modal('hide'); $("#alertInfo4Reload").html("<p class=\"text-success\">App修改成功!</p>"); $('#myAlertModal4Reload').modal('show'); }else{ $("#alertInfo").html("<p class=\"text-error\">App修改失败!</p>"); $('#myAlertModal').modal('show'); } }); }else{ $("#updApp_error_info").html("<p class=\"text-error\">App名称与描述为必填字段!</p>"); } }); $("#btn_delApp").click(function(){ $.getJSON(url,{m:"delApp",appId:$("#del_app_id").val()},function(json){ if(json.result == "1"){ $("#del_app_id").val(""); $('#delAppModal').modal('hide'); $("#alertInfo4Reload").html("<p class=\"text-success\">App删除成功!</p>"); $('#myAlertModal4Reload').modal('show'); }else{ $("#alertInfo").html("<p class=\"text-error\">App删除失败!</p>"); $('#myAlertModal').modal('show'); } }); }); $('#myAlertModal4Reload').on('hidden', function () { location.reload(true);//是的,我承认我偷懒了 }); }); </script> </body> </html>以上就是kFeedback的所有代码,配置文件比较简单,我就不提供了,如有需要,可给我留言。
为了方便大家学习和调试代码,我将完整的工程提供给大家,欢迎斧正
https://github.com/kongit/kFeedback
http://git.oschina.net/kzhou-hrb/kfeedback