• 仿花田:相亲网站 意中人 已在GitHub上开源


         在园友的强烈呼唤下,我还是负责任的分享给大家,因为对代码比较熟悉一下,还是有些问题要说明,不然别人看起来会比较费劲。说实话除了这个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>  

    图片上传的模态对话框:

        <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">&times;</button>
                <h4>上传图片</h4>  
            </div>
            <div class="modal-body">
                <div class="uploadbox" data-count="0" style="display: inline;">
                    <span class="closespan" title="删除照片">&times;</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="删除照片">&times;</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="删除照片">&times;</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="删除照片">&times;</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=" 90px"></select>   <select name="ResidenceCity" id="ResidenceCity" style=" 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  (帮助文档还有其他说明)

    ( 下载后可能又的问题。

        1.缺少引用:请在package1中找缺少的引用。 建议安装Nuget ,会自动给你还原。

        2.不能生产表格。是因为没有EntityFramework.  没有启用codefirst 功能。可以参考博客:http://www.cnblogs.com/stoneniqiu/archive/2013/06/04/3117499.html

         我晚上再补上创建表的SQL命令。 数据库我不传了。

        3.缺少kendo.MVC.dll 这个dll没有包含在package1中,我也奇怪。漏掉了。可以在http://download.csdn.net/my/uploads 下载。 晚上我也会更新到github上去。

        4.缺少引用的dll中,只有Nlog,kendo.MVC,Web.Provider是有用的,其他的缺少的dll可以移除。是因为之前做过其他的测试留下的。

        还有问题可以给我留言

          )

    最新版本:http://pan.baidu.com/s/1pJoiM1P

    DB:http://pan.baidu.com/s/1gdkvvu7

  • 相关阅读:
    Java——通过Java代码启动批处理文件
    成功解决错误1130 Host xxx is not allowed to connect to this MySQL server
    SQL全文索引的作用(转)
    查找不重复记录
    全文索引原理和一个完整的SQL SERVER数据库全文索引的示例(转)
    C# 参考:令人惊喜的泛型委托 Predicate/Func/Action
    moss 外网访问设置
    SQL2000和SQL2005的行转列处理方法
    海量数据库查询
    MSSQL 查询优化二(转)
  • 原文地址:https://www.cnblogs.com/stoneniqiu/p/3456739.html
Copyright © 2020-2023  润新知