在园友的强烈呼唤下,我还是负责任的分享给大家,因为对代码比较熟悉一下,还是有些问题要说明,不然别人看起来会比较费劲。说实话除了这个bootstrap的界面风格和这件事情本身对大家有吸引力之外,内部的逻辑,结构,可能有些捉襟见肘,会让大家见笑,大牛们完全可以略过.现在源码已经让我推到GitHub上面去了。我先从头到尾的介绍一下,然后说一些存在的问题。也希望大家给出更好的建议。
一、结构说明
1.MVC 我先说模型,共有20几张表,支持数据迁移。BaseInfos(基本资料),DetailInfos(详细资料),LoveViews(恋爱观) 应该是做到一张User表中的,脑抽的我把他们分成了三张表,因为他们是分别存储的。下载之后,改成你自己的数据库,运行就能创建新表了。
using System; using System.Collections.Generic; using System.Data.Entity; using Findlover.Migrations; namespace Findlover.Models { public class LoveDb:DbContext { private const string DbNameOrDbConnectionstring = "Data Source=your-pc;Initial Catalog=LoveDB;User ID=sa;Password=code;Persist Security Info=True"; //Persist Security Info=True public DbSet<BaseInfo> BaseInfos { get; set; }// 基本资料 public DbSet<User> Users { get; set; }// 用户 public DbSet<Requirement> Requirements { get; set; }//择偶需求 public DbSet<DetailInfo> DetailInfos { get; set; }//详细资料 public DbSet<LoveView> LoveViews { get; set; }//恋爱观 public DbSet<InfoStatistic> InfoStatistics { get; set; }// 信息统计,统计用户资料的完整度。避免每次都要去三张表数一遍。 public DbSet<UserHot> UserHots { get; set; }//用户的人热度 public DbSet<Praise> Praises { get; set; }//赞 也就是喜欢 public DbSet<State> States { get; set; }// 状态 public DbSet<Iamgbox> Iamgboxes { get; set; }// 相册 public DbSet<Message> Messages { get; set; }//私信 public DbSet<LoginLog> LoginLogs { get; set; }// 登录日志记录 public DbSet<Role> Roles { get; set; }//角色 public DbSet<Hello> Hellos { get; set; }//打招呼 public DbSet<RoleLog> RoleLogs { get; set; }// 角色的日志 public DbSet<Authority> Authoritys { get; set; }// 权限 public DbSet<AdminStatistic> AdminStatistics { get; set; }//管理员操作统计 这个功能没有多大意义 你可拿掉 public DbSet<VisitLog> VisitLogs { get; set; }// 访问记录 public DbSet<Report> Reports { get; set; }//举报 public DbSet<ReportLog> ReportLogs { get; set; }//举报日志 public DbSet<MyLove> MyLoves { get; set; }// 我喜欢的人 public DbSet<DisLove> DisLoves { get; set; }//我不喜欢的人 相当于黑名单 public DbSet<Topic> Topics { get; set; }// 话题 public DbSet<Comment> Comments { get; set; }//评论 public DbSet<EnjoyTopic> EnjoyTopics { get; set; }//感兴趣的话题 public LoveDb() : base(DbNameOrDbConnectionstring) { Database.SetInitializer(new MigrateDatabaseToLatestVersion<LoveDb, Configuration<LoveDb>>()); } } }
2. 控制器: 共有8个控制器,其中BaseController 里面集合一些常用的方法和属性,其他都继承这个控制器,其他看名字你也知道是干啥的,Interactive是对应的Topic,本来是打算一个专门用来做交互的控制器。现在只有话题。但可能让大家恼火的就是,这分的还不细致,不准确。之前自己偷懒,什么方法写在什么控制器没有明确的定义清楚,这对以后维护和扩展造成不方便。
3.视图:主要是基于Bootstrap2,Jquery1.7.2.,form.js 注册的日期控件和Topic的编辑器用的是KendoUi, 小伙伴们可以去换成Ckeditor,而Bootstrap用的地方主要是在
Layout的导航,后台导航,举报、私信、图片上传的模态对话框,在主页面使用它的栅格比较少,因为老是和导航对不齐,就放弃了。或许是我用的不对。Jquery用来交互,获取数据。form.js 用的比较多,很多地方是form提交,没有用MVC自带的Create。原因就是不好看,而且不好控制,ie下面会走样。
二、前端特别说明
这里特别要说明的就是一些函数和窗口的触发
1.图片上传和举报框。
在用户主页的视图和PersonalInfo视图中会有这两个相似的html代码,是用来触发图片上传框的。
<div class="infopercentright">上传4张照片,资料完整度+8% <a class="infotag" id="imgup" href="#imgupload" data-toggle="modal" title="上传照片"><i class="icon-camera"></i></a> </div>
<a class="infotag" id="imgupload" href="#imgupload" data-toggle="modal" title="上传照片"><i class="icon-camera"></i></a>
图片上传的模态对话框:
logs_code_hide('fbd11c84-e456-4cd1-89b4-22d4137a5910',event)" src="/Upload/Images/2013120412/2B1B950FA3DF188F.gif" alt="" /><div id="imgupload" 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> <h4>上传图片</h4> </div> <div class="modal-body"> <div class="uploadbox" data-count="0" style="display: inline;"> <span class="closespan" title="删除照片">×</span> <div class="imgcontainer"> <div class="add">+</div> <div class="stt">点击上传</div> </div> <span class="infospan"> <img src='../../Content/Photos/loading.gif' />正在上传...</span> </div> <div class="uploadbox" data-count="0" > <span class="closespan" title="删除照片">×</span> <div class="imgcontainer"> <div class="add">+</div> <div class="stt">点击上传</div> </div> <span class="infospan"> <img src='../../Content/Photos/loading.gif' />正在上传...</span> </div> <div class="uploadbox"data-count="0" > <span class="closespan" title="删除照片">×</span> <div class="imgcontainer"> <div class="add">+</div> <div class="stt">点击上传</div> </div> <span class="infospan"> <img src='../../Content/Photos/loading.gif' />正在上传...</span> </div> <div class="uploadbox"data-count="0" > <span class="closespan" title="删除照片">×</span> <div class="imgcontainer"> <div class="add">+</div> <div class="stt">点击上传</div> </div> <span class="infospan"> <img src='../../Content/Photos/loading.gif' />正在上传...</span> </div> <div class="inputdiv"><input type="text" disabled="disabled" id="Remark" name="Remark" placeholder="补充说明下~"/></div> </div> <div class="modal-footer"> 上传大小在8k-10M之间 <span class="imguploadmessage"></span> <button class="btn btn-success " disabled="disabled" id="imgsubmit">发布</button> <form action="/User/UpLoadPhoto" method="POST" enctype="multipart/form-data" name="ImgForm" id="ImgForm"> <input type="file" name="file" id="imgFlie" required="required" /> <input type="submit" name="subt" value="上传图片" /> </form> </div> </div>View Code
生成的图像如下:
举报也同样的做法: 但举报需要提供更多的信息,所以加了很多data属性。用脚本触发对话框
<span class="report" data-reportType="personal" data-relateId="@Model.UserId" data-content="@Model.UserName" data-userId="@Model.UserId" data-userName="@Model.UserName" data-sextag="@ViewBag.Sex">拉黑/举报</span>
//举报框----------------------report-------------------------------------------- $(".report").each(function () { $(this).click(function () { var id = $(this).attr("data-userId"); var name = $(this).attr("data-userName"); var sextag = $(this).attr("data-sextag");//对方性别 var type = $(this).attr("data-reportType"); var relateId = $(this).attr("data-relateId"); var content = $(this).attr("data-content"); $("#reportedname").html(name).attr("data-id", id);//被举报人id $(".reportinfo span").html(sextag); $("#reportName").html(name); $("#reporttype").val(type);//举报类型 $("#reporttype").attr("data-relateId", relateId); $("#reporttype").attr("data-content", content); //值是举报或者拉黑这个人 初始化 $(".divrow:eq(1)").show(); $("#reportcontent").hide(); $(".reportmessage").html(); $("#reportcontent").html(content); //不同类型,不同对话 switch (type) { case "personal": $(".divrow:eq(1)").hide(); break; case "message": $("#reportType").html("发给我的私信"); $("#IsReport").attr("checked", "checked"); $("#reportcontent").show(); break; default: } $("#IsReport").click(function () { var ss = $(this).is(':checked'); if (ss) { $(".divrow:eq(1),#reportcontent").slideDown(); } else { $(".divrow:eq(1),#reportcontent").slideUp(); } }); $('#report_box').modal('show'); }); });
举报框:
我想大家也明白了,就是这样的一回事,脚本没有压缩,大家可以去看。其他像 喜欢,私信,都是一个道理,定义好一个样式,主要是样式名称,在layout里面写脚本,这样就很方便了。
2.form提交:
用form.js,能很好的控制整个提交过程,BeginForm,隐藏的form,图片上传的form。
编辑基本资料:
<div class="infoedit hide" > @using (Html.BeginForm("UpdateBaseInfo", "User", FormMethod.Post, new { name = "Form1", id = "Form1" })) { <table class=""> <tr> <td>身高:</td> <td> <select name="Height" id="Height"></select></td> <td>学历:</td> <td> <select name="Education" id="Education"></select></td> </tr> <tr> <td>月收入:</td> <td> <select name="MonthlyIncome" id="MonthlyIncome"></select></td> <td>毕业院校:</td> <td> <input type="text" name="School" id="School" value="@Model.BaseInfo.School"/></td> </tr> <tr> <td>工作单位:</td> <td> <select name="Company" id="Company"></select></td> <td>居住地:</td> <td> <select name="ResidenceProvince" id="ResidenceProvince" style="width: 90px"></select> <select name="ResidenceCity" id="ResidenceCity" style="width: 90px"></select></td> </tr> <tr> <td>目前职位:</td> <td> <select name="Position" id="Position"></select></td> <td>行业:</td> <td> <input type="text" id="Profession" readonly="readonly" name="Profession" value="生产/工艺/制造"></td> </tr> <tr> <td>当前状态:</td> <td> <select name="State" id="State"></select></td> </tr> </table> <button type="submit" class="btn btn-info"> 保存</button> <span class="cancel-eidt">取消编辑</span> } </div>View Code
这样的方式用了很多,脚本基本上是下面这样,还可以加上beforeSend,complete. 其他项获取json数据这些,就是家常便饭了,没什么说的。
$('#Form1').submit(function () { $(this).ajaxSubmit(options); return false; }); var options = { dataType: 'json', success: processJson, }; function processJson(data) { // 还是返回json的好,再去更新。 $(".infowrap").show(); //...... $("#sProfession").html(data.Profession); $("#sPosition").html(data.Position); $("#sSchool").html(data.School); $("#sState").html(data.State); }
二、后台逻辑
其实这个部分是最没有含金量的,只要大家清楚业务流程了,逻辑再怎么也能写出来。我也没有用AOP来记录日志或者处理异常,用IOC/DI来解耦。这也是我的弱点。所以我就简单说明下。
1.符合基本条件的用户: 因为是相亲交友,所以要筛选用户。在BaseController中有个GetBaseUsers
/// <summary> /// 获取基本的异性集合 不可以允许未审核图片的人进来! /// </summary> /// <param name="id"></param> /// <returns></returns> protected IEnumerable<User> GetBaseUsers(int id) { var lz = LoveDb.One((User n) => n.UserId == id); var dislikelist = GetDislikeList();//过滤的名单。 过掉我不喜欢的人。 //还要过掉不喜欢我的人? if (lz != null) { //选出所有异性中图片审核过的,资料审核过的人,且资料开放,没有被禁止的人 return LoveDb.UserAll() .Where(n => n.Sex != lz.Sex && n.IsVerified && n.IsVerifiedImg && n.IsOpen && n.Enable == 1&&!dislikelist.Contains(n.UserId)) .ToList(); } return null; }
2.推荐的用户:推荐,动态,搜索的基本用户都是来自这里。 你可以稍微改下代码让他可以选择同性。而推荐和动态的用户 还需要满足一个用户的基本择偶要求。 就是比如我择偶的要求是
158以上,江苏,22岁以上。那出现在这两个页面的用户就必须满足这三个条件是硬性指标,但学历,薪水是可以不匹配的。所以在推荐页面是这样的:
public ActionResult RecommendPage() { var id = CheckValid(); if (id == -1 || Session["uid"] == null) { return RedirectToAction("Logon", "User");//不知道有没有效果 } var require = LoveDb.One((Requirement r) => r.UserId == id); var baseusers = GetBaseUsers(id).Where(n => PartMathUser(require, LoveDb.GetUninUser(n.UserId)));//找出所有正规的异性符合需求的。 ViewBag.Sex = GetMyself().User.Sex == "man" ? "她" : "他"; var userlist = baseusers.Select(baseuser => GetRecommendUser(baseuser.UserId)).ToList(); return PartialView(userlist.OrderByDescending(n => n.Rate.TotalRate)); }
partMathUser 就是一个专门用来比对的函数。原理就是捞出来一个一个比对,三个条件全部对了就是true。搜索也是一个道理,前台把条件传过来,后台来处理。我想这个地方应该是很有优化的空间。
3.推荐度和热度
计算推荐度,就是看两者资料的匹配度,而热度大家可以去看模型就知道了,就是统计数据再综合排名。换成百分率。
/// <summary> /// 用户热度,相互符合度,生活观契合度 /// </summary> /// <param name="selfid">本人</param> /// <param name="otherid">比较者</param> /// <returns></returns> public RecommendRate GetRecommendRate(int selfid, int otherid) { var usersum = LoveDb.UserAll().Count; //得到两个人的需求表 来计算相互符合度 var myre = LoveDb.One((Requirement r) => r.UserId == selfid); var youre = LoveDb.One((Requirement r) => r.UserId == otherid); var yourUnin = LoveDb.GetUninUser(otherid); var me = GetMyself(); var rr = new RecommendRate { //用户热度排名UserHotRate UserHotRate = (double)(usersum - LoveDb.UserHotAllDes().FindIndex(n => n.UserId == otherid)) / usersum, ForMeRate = TomeRate(yourUnin, myre), ForOtherRate = TomeRate(me, youre), LoveViwRate = GetLoveViewRate(yourUnin, me) }; rr.TotalRate = (rr.UserHotRate + rr.LoveViwRate + rr.ForMeRate + rr.ForOtherRate) / 4; return rr; }
其他就没啥好说的,各种return json,return partview() 我想这个是容易看明白的。
三、数据层
1.LoveDb仓库,像单例模式,获取实例,用反射封装了Add,Delete,One,All等常用方法。
public class LoveDbRepository { private static LoveDbRepository _loveDb; private LoveDbRepository() { } public static LoveDbRepository GetIntance() { return _loveDb ?? (_loveDb = new LoveDbRepository()); } #region 常规方法 Add Delete All One Ones LastOne #region Add public void Add<T>(T t) { using (var loveDb = new LoveDb()) { switch (t.GetType().Name) { case "User": loveDb.Users.Add(t as User); break; case "BaseInfo": loveDb.BaseInfos.Add(t as BaseInfo); break; case "Requirement": loveDb.Requirements.Add(t as Requirement); break; case "DetailInfo": loveDb.DetailInfos.Add(t as DetailInfo); break; case "LoveView": loveDb.LoveViews.Add(t as LoveView); break; case "InfoStatistic": loveDb.InfoStatistics.Add(t as InfoStatistic); break; case "UserHot": loveDb.UserHots.Add(t as UserHot); break; //..................break; } loveDb.SaveChanges(); } }
........................
2.数据迁移。修改model,自动更新数据库。这个也是老生常谈了,也可以看博客 http://www.cnblogs.com/stoneniqiu/archive/2013/06/04/3117499.html
namespace Findlover.Migrations { using System.Data.Entity; using System.Data.Entity.Migrations; internal sealed class Configuration<TContext> : DbMigrationsConfiguration<TContext> where TContext : DbContext { public Configuration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true; } protected override void Seed(TContext context) { } } }
再就是Lambda ,linq。到处都是,没有写一句sql语句。这个没什么说的。
四、问题和扩展
大大小小的问题还是蛮多的,兼容性的,脚本的,逻辑的,性能的,结构的。这些都还要慢慢的改。所以有问题不要惊奇,是很正常的。像上传和文本编辑应该换uploadfy和ckeditor控件。这个版本还么有加入分页,或者换成流行的滚动加载。增加一些好玩的功能,这些就要看你们的了,尽情的去折腾吧。小丘比特谢谢你!
祝你幸福~
五、说下开源
CodePlex和gitHub合作了,CodePlex可以用git上传,而git也变成了这个windows风格: 方便大方好用。
在github的工程名称叫FindLover(说DreamLover更合适些吧),因为FindLover是先取的,名字想了很久才叫意中人。
总结下:现在MVC5已经出来了,我还在看MVC4,EntityFrameWork6也来了,还有VS2013,.NetFrameWork4.5。Bootstrap也到3了。Html5动画研究的还不咋地,还有各种前端的,后端的框架,目不暇接,人艰不拆啊。
新的技术提供了一些更好的方式,但是你更需要的是一个方向,一个ideal,几个一条路上的小伙伴。
需要源码的同学请 猛击这里!!!! https://github.com/stoneniqiu/FindLover (帮助文档还有其他说明)
老本都拿出来了,挥泪求赞啊~