程序员的自我救赎---1.4.1: 核心框架讲解(DAL)

《前言》

《目录》

(一) Winner2.0 框架基础分析

(二) 短信中心

(三)SSO单点登录

(四)PLSQL报表系统

(五)钱包系统

(六)GPU支付中心

(七)权限系统

(八)监控系统

(九)会员中心

(十)消息中心

(十一)Winner前端框架与RPC接口规范讲解

(十二)上层应用案例

(十三)番外篇

《核心框架讲解》

之前在《Winner2.0框架解决方案命分层规范》 有讲到过Winner框架最重要的三个程序集分别是:

Winner.Framework.Core       (核心类dll)

Winner.Framework.Encrypt   (加密类dll)

Winner.Framework.Utils         (工具类dll)

这三个死核心类库,每一层都引用这三个程序集。如果是做MVC的web开发我们还会有一个Winner.Framework.MVC   (前端dll)

每一层都继承框架的基类如下:

DataAcces: 继承  “DataAccessBase”

Entities: 没有单独可基础的核心框架类,可直接引用工具类

Facade:      继承:“FacadeBase”

APP(MVC):  继承:“TopControllerBase”

我们重点讲一下,“DataAccessBase”,“FacadeBase”,“TopControllerBase” 这三个基类。

其实前面在 《代码生成器的使用》和《事务的使用》两篇文章中就分别讲到过“DataAccessBase” 和 "FacadeBase"  。

我们先来分析DataAccessBase我们看一下在我们真实开发中关于数据库访问代码的的使用。建议复制到VS中去看,不然在博客中确实不好看懂。

/***********************代码生成器生成的 充值表数据操作************
*
* Data Access Layer Of Winner Framework
* FileName : Tdct_Recharge.generate.cs
* Version : V 1.1.0
* Author:架构师 曾杰(Jie)
* E_Mail : [email protected]
* Tencent QQ:554044818
* Blog : http://www.cnblogs.com/fineblog/
* CreateTime : 2017-10-11 18:06:04
*
***************************************************/
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using Winner.Framework.Core;
using Winner.Framework.Core.DataAccess;
using Winner.Framework.Core.DataAccess.Oracle;
using Winner.Framework.Utils;
using Winner.DCTS.Entities;
namespace Winner.DCTS.DataAccess
{
    /// <summary>
    /// Data Access Layer Object Of Tdct_Recharge
    /// </summary>
    public partial class Tdct_Recharge : DataAccessBase
    {
        #region 默认构造

        public Tdct_Recharge() : base() { }

        public Tdct_Recharge(DataRow dataRow)
            : base(dataRow) { }

        #endregion 默认构造

        #region 对应表结构的常量属性

        public const string _RECHARGE_ID="RECHARGE_ID";
        public const string _PAY_TYPE="PAY_TYPE";
        public const string _AMOUNT="AMOUNT";
        public const string _ACCOUNT="ACCOUNT";
        public const string _USER_ID="USER_ID";
        public const string _STATUS="STATUS";
        public const string _CREATE_TIME="CREATE_TIME";
        public const string _REMARKS="REMARKS";

        public const string _TABLENAME="Tdct_Recharge";
        #endregion 对应表结构的常量属性

        #region 公开属性

        /// <summary>
        /// 充值记录ID
        /// [default:0]
        /// </summary>
        public int RechargeId
        {
            get { return Convert.ToInt32(DataRow[_RECHARGE_ID]); }
            set { setProperty(_RECHARGE_ID,value); }
        }
        /// <summary>
        /// 支付类型 1支付宝,2银行卡,3微信
        /// [default:0]
        /// </summary>
        public int PayType
        {
            get { return Convert.ToInt32(DataRow[_PAY_TYPE]); }
            set { setProperty(_PAY_TYPE,value); }
        }
        /// <summary>
        /// 充值金额
        /// [default:0]
        /// </summary>
        public decimal Amount
        {
            get { return Convert.ToDecimal(DataRow[_AMOUNT]); }
            set { setProperty(_AMOUNT,value); }
        }
        /// <summary>
        /// 充值账号
        /// [default:string.Empty]
        /// </summary>
        public string Account
        {
            get { return DataRow[_ACCOUNT].ToString(); }
            set { setProperty(_ACCOUNT,value); }
        }
        /// <summary>
        /// 充值用户
        /// [default:0]
        /// </summary>
        public int UserId
        {
            get { return Convert.ToInt32(DataRow[_USER_ID]); }
            set { setProperty(_USER_ID,value); }
        }
        /// <summary>
        /// 充值状态 0等待银行到账,1已经到账,2操作失败
        /// [default:0]
        /// </summary>
        public RechargeStatus Status
        {
            get { return (RechargeStatus)Convert.ToInt32(DataRow[_STATUS]); }
            set { setProperty(_STATUS,(int)value); }
        }
        /// <summary>
        /// 充值时间
        /// [default:new DateTime()]
        /// </summary>
        public DateTime CreateTime
        {
            get { return Convert.ToDateTime(DataRow[_CREATE_TIME].ToString()); }
        }
        /// <summary>
        /// 备注
        /// [default:string.Empty]
        /// </summary>
        public string Remarks
        {
            get { return DataRow[_REMARKS].ToString(); }
            set { setProperty(_REMARKS,value); }
        }

        #endregion 公开属性

        #region 私有成员

        protected override string TableName
        {
            get { return _TABLENAME; }
        }

        protected override DataRow BuildRow()
        {
            DataTable dt = new DataTable(_TABLENAME);
            dt.Columns.Add(_RECHARGE_ID, typeof(int)).DefaultValue = 0;
            dt.Columns.Add(_PAY_TYPE, typeof(int)).DefaultValue = 0;
            dt.Columns.Add(_AMOUNT, typeof(decimal)).DefaultValue = 0;
            dt.Columns.Add(_ACCOUNT, typeof(string)).DefaultValue = string.Empty;
            dt.Columns.Add(_USER_ID, typeof(int)).DefaultValue = 0;
            dt.Columns.Add(_STATUS, typeof(int)).DefaultValue = 0;
            dt.Columns.Add(_CREATE_TIME, typeof(DateTime)).DefaultValue = new DateTime();
            dt.Columns.Add(_REMARKS, typeof(string)).DefaultValue = string.Empty;

            return dt.NewRow();
        }

        #endregion 私有成员

        #region 常用方法

        protected bool DeleteByCondition(string condition)
        {
            string sql = @"DELETE FROM TDCT_RECHARGE WHERE " + condition;
            return base.DeleteBySql(sql);
        }

        public bool Delete(int rechargeId)
        {
            string condition = "RECHARGE_ID=:RECHARGE_ID";
            AddParameter(_RECHARGE_ID, rechargeId);
            return DeleteByCondition(condition);
        }

        public bool Delete()
        {
            string condition = "RECHARGE_ID=:RECHARGE_ID";
            AddParameter(_RECHARGE_ID, RechargeId);
            return DeleteByCondition(condition);
        }

        public bool Insert()
        {
            RechargeId=base.GetSequence("SELECT SEQ_TDCT_RECHARGE.NEXTVAL FROM DUAL");
string sql=@"INSERT INTO
TDCT_RECHARGE(
  RECHARGE_ID,
  PAY_TYPE,
  AMOUNT,
  ACCOUNT,
  USER_ID,
  STATUS,
  REMARKS)
VALUES(
  :RECHARGE_ID,
  :PAY_TYPE,
  :AMOUNT,
  :ACCOUNT,
  :USER_ID,
  :STATUS,
  :REMARKS)";
            AddParameter(_RECHARGE_ID,DataRow[_RECHARGE_ID]);
            AddParameter(_PAY_TYPE,DataRow[_PAY_TYPE]);
            AddParameter(_AMOUNT,DataRow[_AMOUNT]);
            AddParameter(_ACCOUNT,DataRow[_ACCOUNT]);
            AddParameter(_USER_ID,DataRow[_USER_ID]);
            AddParameter(_STATUS,DataRow[_STATUS]);
            AddParameter(_REMARKS,DataRow[_REMARKS]);
            return base.InsertBySql(sql);
        }

        public bool Update()
        {
            return UpdateByCondition(string.Empty);
        }

        protected bool UpdateByCondition(string condition)
        {
            //移除主键标记
            ChangePropertys.Remove(_RECHARGE_ID);

            if (ChangePropertys.Count == 0)
            {
                return true;
            }

            StringBuilder sql = new StringBuilder();
            sql.AppendLine("UPDATE TDCT_RECHARGE SET");
            while (ChangePropertys.MoveNext())
            {
                 sql.AppendFormat(" {0}{1}=:{1} ", (ChangePropertys.CurrentIndex == 0 ? string.Empty : ","), ChangePropertys.Current);
                AddParameter(ChangePropertys.Current, DataRow[ChangePropertys.Current]);
            }
            sql.AppendLine(" WHERE RECHARGE_ID=:RECHARGE_ID");
            AddParameter(_RECHARGE_ID, DataRow[_RECHARGE_ID]);
            if (!string.IsNullOrEmpty(condition))
                sql.AppendLine(" AND " + condition);

            bool result = base.UpdateBySql(sql.ToString());
            ChangePropertys.Clear();
            return result;
        }

        protected bool SelectByCondition(string condition)
        {
            string sql = @"
SELECT
  RECHARGE_ID,
  PAY_TYPE,
  AMOUNT,
  ACCOUNT,
  USER_ID,
  STATUS,
  CREATE_TIME,
  REMARKS
FROM TDCT_RECHARGE
WHERE " + condition;
            return base.SelectBySql(sql);
        }

        public bool SelectByPK(int rechargeId)
        {
            string condition = "RECHARGE_ID=:RECHARGE_ID";
            AddParameter(_RECHARGE_ID, rechargeId);
            return SelectByCondition(condition);
        }
        public Tnet_User Get_Tnet_User_ByUserId()
        {
            Tnet_User da=new Tnet_User();
            da.SelectByPK(UserId);
            return da;
        }

        #endregion 常用方法

        //提示:此类由代码生成器生成,如无特殊情况请不要更改。如要扩展请在外部同名类中扩展
    }

    /// <summary>
    /// Data Access Layer Object Collection Of Tdct_Recharge
    /// </summary>
    public partial class Tdct_RechargeCollection : DataAccessCollectionBase
    {
        #region 默认构造

        public Tdct_RechargeCollection() { }

        public Tdct_RechargeCollection(DataTable table)
            : base(table) { }

        #endregion 默认构造

        #region 私有成员
        protected override DataAccessBase GetItemByIndex(int index)
        {
            return new Tdct_Recharge(DataTable.Rows[index]);
        }

        protected override DataTable BuildTable()
        {
            return new  Tdct_Recharge().CloneSchemaOfTable();
        }

        protected override string TableName
        {
            get { return Tdct_Recharge._TABLENAME; }
        }

        protected bool ListByCondition(string condition)
        {
            string sql = @"
SELECT
  RECHARGE_ID,
  PAY_TYPE,
  AMOUNT,
  ACCOUNT,
  USER_ID,
  STATUS,
  CREATE_TIME,
  REMARKS
FROM TDCT_RECHARGE
WHERE " + condition;
            return base.ListBySql(sql);
        }

        public bool ListByRechargeId(int rechargeId)
        {
            string condition = "RECHARGE_ID=:RECHARGE_ID";
            AddParameter(Tdct_Recharge._RECHARGE_ID, rechargeId);
            return ListByCondition(condition);
        }

        public bool ListAll()
        {
            string condition = "1=1";
            return ListByCondition(condition);
        }

        public bool DeleteByCondition(string condition)
        {
            string sql = "DELETE FROM TDCT_RECHARGE WHERE " + condition;
            return DeleteBySql(sql);
        }
        #endregion

        #region 公开成员
        public Tdct_Recharge this[int index]
        {
            get
            {
                return new Tdct_Recharge(DataTable.Rows[index]);
            }
        }

        public bool DeleteAll()
        {
            return this.DeleteByCondition(string.Empty);
        }

        #region Linq

        public Tdct_Recharge Find(Predicate<Tdct_Recharge> match)
        {
            foreach (Tdct_Recharge item in this)
            {
                if (match(item))
                    return item;
            }
            return null;
        }
        public Tdct_RechargeCollection FindAll(Predicate<Tdct_Recharge> match)
        {
            Tdct_RechargeCollection list = new Tdct_RechargeCollection();
            foreach (Tdct_Recharge item in this)
            {
                if (match(item))
                    list.Add(item);
            }
            return list;
        }
        public bool Contains(Predicate<Tdct_Recharge> match)
        {
            foreach (Tdct_Recharge item in this)
            {
                if (match(item))
                    return true;
            }
            return false;
        }

        public bool DeleteAt(Predicate<Tdct_Recharge> match)
        {
            BeginTransaction();
            foreach (Tdct_Recharge item in this)
            {
                item.ReferenceTransactionFrom(Transaction);
                if (!match(item))
                    continue;
                if (!item.Delete())
                {
                    Rollback();
                    return false;
                }
            }
            Commit();
            return true;
        }
        #endregion Linq
        #endregion

        //提示:此类由代码生成器生成,如无特殊情况请不要更改。如要扩展请在外部同名类中扩展
    }
} 
/***********************由技术员编写****************************
*
* Data Access Layer Of Winner Framework
* FileName : Tdct_Recharge.extension.cs
* Version : V 1.1.0
* Author:架构师 曾杰(Jie)
* E_Mail : [email protected]
* Tencent QQ:554044818
* Blog : http://www.cnblogs.com/fineblog/
* CreateTime : 2017-10-11 17:55:39
*
***************************************************/
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using Winner.Framework.Core;
using Winner.Framework.Core.DataAccess;
using Winner.Framework.Core.DataAccess.Oracle;
using Winner.Framework.Utils;
using Winner.DCTS.Entities;
using Winner.DCTS.Entities.enums;

namespace Winner.DCTS.DataAccess
{
    /// <summary>
    /// Data Access Layer Object Of Tdct_Recharge
    /// </summary>
    public partial class Tdct_Recharge : DataAccessBase
    {
        //Custom Extension Class
        public bool Delete(int rechargeId, int userId)
        {
            string condition = "RECHARGE_ID=:RECHARGE_ID AND USER_ID=:USER_ID AND STATUS=0";
            AddParameter(_RECHARGE_ID, rechargeId);
            AddParameter(_USER_ID, userId);
            return DeleteByCondition(condition);
        }

        public bool SelectById(int rechargeId)
        {
            string condition = "RECHARGE_ID=:RECHARGE_ID AND STATUS=:STATUS";
            AddParameter(_RECHARGE_ID, rechargeId);
            AddParameter(_STATUS, RechargeStatus.等待充值.GetHashCode());
            return SelectByCondition(condition);
        }
    }

    /// <summary>
    /// Data Access Layer Object Collection Of Tdct_Recharge
    /// </summary>
    public partial class Tdct_RechargeCollection : DataAccessCollectionBase
    {
        public bool ListByUserId(int userId)
        {
            string condition = " USER_ID = :USER_ID ORDER BY RECHARGE_ID DESC ";
            AddParameter("USER_ID", userId);
            return ListByCondition(condition);
        }

        /// <summary>
        /// 查询列表
        /// </summary>
        /// <param name="status"></param>
        /// <param name="paytype"></param>
        /// <returns></returns>
        public bool ListByKeyWord(string keyword,int status,int paytype)
        {
            string sql = "SELECT TR.*,VU.USER_CODE,VU.USER_NAME FROM TDCT_RECHARGE TR JOIN VNET_USER VU ON TR.USER_ID=VU.USER_ID  WHERE 1=1 ";

            if(!string.IsNullOrEmpty(keyword))
            {
                sql += " AND( TR.ID=:ID";
                sql += " OR VU.USER_NAME=:USERNAME)";
                AddParameter("ID", keyword);
                AddParameter("USERNAME", keyword);
            }

            if (status>=0)
            {
                sql += " AND TR.STATUS=:STATUS";
                AddParameter("STATUS", status);
            }

            if(paytype>=0)
            {
                sql += " AND TR.PAY_TYPE=:PAYTYPE";
                AddParameter("PAYTYPE",paytype);
            }

            sql += " ORDER BY TR.RECHARGE_ID DESC";

            return ListBySql(sql);
        }

        //Custom Extension Class
    }
}

对于项目结构不理解的需要先看《Winner2.0框架解决方案命分层规范》  才能看懂,为什么我们会这样写。 我想了想关于DataAccessBase,我们还是又浅入深的去讲。

先从最最简单的 AddParameter() 这个方法开始讲起,明眼一看就知道这是一个 “参数化” 传值,至于参数化传值有什么好处就自行百度吧,我看到很多公司的项目本身可以封装

一层参数化方法,但是却“痴迷”于用 存储过程 却解决参数化问题,然后放大存储过程“预编译”的优势,其实这个没有必要。无论是哪种数据库都是支持C# 参数化 操作的。

我们看看AddParameter() 这个方法是怎么实现的:

   #region AddParameter

        /// <summary>
        /// 添加DAO.NET的SqlCommand命令对象参数
        /// </summary>
        /// <param name="parameterName">参数名称</param>
        /// <param name="value">参数值</param>
        protected DbParameter AddParameter(string parameterName, object value)
        {
            if (string.IsNullOrEmpty(parameterName))
            {
                throw new Exception("传入的参数名称为空");
            }
            if (string.IsNullOrEmpty(DBFactory.ParameterSign))
            {
                throw new Exception("未指定参数符合(例如:Oracle::、SqlServer:@....)");
            }
            if (!parameterName.StartsWith(this.DBFactory.ParameterSign))
            {
                parameterName = this.DBFactory.ParameterSign + parameterName;
            }
            DbParameter parm = DBFactory.DBProvider.CreateParameter();
            parm.ParameterName = parameterName;
            parm.Value = value;
            AddParameter(parm);
            return parm;
        }

        /// <summary>
        /// 添加DAO.NET的SqlCommand命令对象参数
        /// </summary>
        /// <param name="parameterName">参数名称</param>
        /// <param name="direction">参数模式</param>
        /// <param name="func">处理返回值函数</param>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// //接收的参数
        /// int id;
        /// AddParameter("Id", ParameterDirection.ReturnValue, value => id = value.ConvertTo<int>());
        /// ]]>
        /// </code>
        /// </example>
        protected DbParameter AddParameter(string parameterName, ParameterDirection direction, Func<DbParameter, object> func)
        {
            if (string.IsNullOrEmpty(parameterName))
            {
                throw new Exception("传入的参数名称为空");
            }
            if (string.IsNullOrEmpty(DBFactory.ParameterSign))
            {
                throw new Exception("未指定参数符合(例如:Oracle::、SqlServer:@....)");
            }
            if (!parameterName.StartsWith(this.DBFactory.ParameterSign))
            {
                parameterName = this.DBFactory.ParameterSign + parameterName;
            }
            DbParameter parm = DBFactory.DBProvider.CreateParameter();
            parm.ParameterName = parameterName;
            parm.Direction = direction;
            parm.Value = string.Empty;
            parm.Size = 2000;
            AddParameter(parm);
            Parameters.OutValuesParamerer.Add(parm, func);
            return parm;
        }

        /// <summary>
        /// 添加DAO.NET的SqlCommand命令对象参数
        /// </summary>
        /// <param name="parameterName">参数名称</param>
        /// <param name="func">处理返回值函数</param>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// //接收的参数
        /// int id;
        /// AddParameter("Id", value => id = value.ConvertTo<int>());
        /// ]]>
        /// </code>
        /// </example>
        protected DbParameter AddParameter(string parameterName, Func<DbParameter, object> func)
        {
            return AddParameter(parameterName, ParameterDirection.Output, func);
        }
        /// <summary>
        /// 添加DAO.NET的SqlCommand命令对象参数
        /// </summary>
        /// <param name="parm">参数</param>
        protected DbParameter AddParameter(DbParameter parm)
        {
            if (this.Parameters == null)
            {
                this.Parameters = new ParameterCollection(DBFactory.DBProvider);
            }
            DbParameter item = this.Parameters.Find(s => s.ParameterName == parm.ParameterName);
            if (item != null)
            {
                this.Parameters.Remove(item);
            }
            this.Parameters.Add(parm);
            return parm;
        }
        #endregion

每种数据库语法的不同,对应的参数化符合自然不同。Sqlerver是用“@”,Oracle是用“:”。这里阿杰用了“工厂模式”来解决这个问题。

这里我们贴出OracleProvider.cs类来作为示例:

using System.Configuration;
using System.Data.Common;
using System.Data.OracleClient;
using Winner.Framework.Core.CustomException;
using Winner.Framework.Utils;

namespace Winner.Framework.Core.DataAccess.Oracle
{
    /// <summary>
    /// Oracle 配置工厂
    /// </summary>
    internal class OracleProvider : DBFactory
    {
        private OracleProvider() { }

        //static OracleFactory() { }

        /// <summary>
        /// Oracle操作工厂实例
        /// </summary>
        public static readonly OracleProvider Instance = new OracleProvider();

        /// <summary>
        /// 数据库工厂
        /// </summary>
        internal override DbProviderFactory DBProvider
        {
            get { return OracleClientFactory.Instance; }
        }

        /// <summary>
        /// 数据库连接字符串
        /// </summary>
        internal override string GetConnectionString()
        {
            ConnectionStringSettings conn = ConfigurationManager.ConnectionStrings["Winner.Framework.Oracle.ConnectionString"];
            if (conn == null)
            {
                ConnectionStringException ex = new ConnectionStringException("AppConfig.ConnectionStrings节点未配置Winner.Framework.Oracle.ConnectionString连接字符串");
                throw ex;
            }
            return conn.ConnectionString;
        }

        /// <summary>
        /// 参数标识符
        /// </summary>
        public override string ParameterSign
        {
            get
            {
                return ":";
            }
        }

        /// <summary>
        /// 通用数据库的SQL语句帮助类
        /// </summary>
        public override SQLHelper SQLHelper
        {
            get { return SQLHelperProvider.Instance; }
        }
    }
}

  

还有工厂类的代码:

using System;
using System.Data.Common;
using Winner.Framework.Core.CustomException;
using Winner.Framework.Utils;

namespace Winner.Framework.Core.DataAccess
{
    /// <summary>
    ///  配置工厂
    /// </summary>
    public abstract class DBFactory
    {
        /// <summary>
        /// 数据库工厂
        /// </summary>
        internal abstract DbProviderFactory DBProvider { get; }

        private string _connectionString;
        /// <summary>
        /// 数据库连接字符串
        /// </summary>
        internal string ConnectionString
        {
            get
            {
                if (string.IsNullOrEmpty(_connectionString))
                {
                    string temp = GetConnectionString();
                    if (temp.Empty())
                    {
                        var connectionStringException = new ConnectionStringException("未获取到数据库连接字符串");
                        Log.Error(connectionStringException);
                        throw connectionStringException;
                    }
                    if (AppSettingsProvider.ConnectionStringEncrypt)
                    {
                        try
                        {
                            temp = Winner.Framework.Encrypt.Hardware.Decode(temp);
                        }
                        catch (Exception ex)
                        {
                            Log.Warn("配置字符串:" + temp);
                            var connectionStringException = new ConnectionStringException("数据库连接字符串解密失败,请确保硬件设备已分配权限,请联系架构师Jie[QQ:554044818]!" + ex.Message, ex);
                            Log.Error(connectionStringException);
                            throw connectionStringException;
                        }
                    }
                    _connectionString = temp;
                }
                return _connectionString;
            }
        }

        /// <summary>
        /// 设置连接字符串
        /// </summary>
        /// <param name="str"></param>
        public void SetConnectionString(string str)
        {
            _connectionString = str;
        }
        /// <summary>
        /// 重置连接字符串
        /// </summary>
        public void ResetConnectionString()
        {
            _connectionString = GetConnectionString();
        }
        /// <summary>
        /// 设置连接字符串
        /// </summary>
        internal abstract string GetConnectionString();

        /// <summary>
        /// 参数标识符号
        /// </summary>
        public abstract string ParameterSign { get; }

        /// <summary>
        /// SQL 帮助类
        /// </summary>
        public abstract SQLHelper SQLHelper { get; }
    }
}

在我们团队内部属于严格要求必须使用参数化。在此之前我们会使用SQL拼接,当然框架现在也是支持参数拼接的,只是拼接在防sql注入上需要严谨对待,下面我

贴一下,框架封装的sql拼接工具类:

using System;
using System.Drawing;
using System.IO;
using Winner.Framework.Encrypt;
namespace Winner.Framework.Core.DataAccess
{
    /// <summary>
    /// 通用数据库的SQL语句帮助类
    /// </summary>
    public abstract class SQLHelper
    {
        /// <summary>
        /// 此类作为静态方式使用不能在其他程序集实例化
        /// </summary>
        internal SQLHelper()
        {
        }

        /// <summary>
        /// 美化SQL,去掉\t\n\r及多余空格
        /// </summary>
        /// <param name="sql">SQL语句</param>
        /// <returns></returns>
        public string BeautifulSql(string sql)
        {
            sql = sql.Replace("\r", " ").Replace("\t", " ").Replace("\n", " ");
            while (sql.IndexOf("  ") >= 0)
            {
                sql = sql.Replace("  ", " ");
            }
            sql = sql.Replace(", ", ",");
            return sql.Trim();
        }

        /// <summary>
        /// SQL参数 敏感处理
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private string SQLParameterHandle(object obj)
        {
            string value = string.Empty;
            if (obj == null)
            {
                return string.Empty;
            }
            value = obj.ToString().Replace("\r", " ").Replace("\t", " ").Replace("\n", " ");
            while (value.IndexOf("  ") >= 0)
            {
                value = value.Replace("  ", " ");
            }
            return value.Replace(", ", ",").Replace("‘", "‘‘");
        }

        /// <summary>
        /// 转换为SQL参数字符串,如:123 转换成 ‘123‘
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public string ToSQLParameter(object obj)
        {
            string value = SQLParameterHandle(obj);
            return string.Format("‘{0}‘", value);
        }

        /// <summary>
        /// 按时间查询的Berween And 语句,如:Age Between 15 ADN 18
        /// </summary>
        /// <param name="columnName">根据查询的列</param>
        /// <param name="begTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>String类型的SQL语句</returns>
        public string ToSQLBetween(string columnName, DateTime begTime, DateTime endTime)
        {
            return string.Format(" {0} Between {1} ADN {2} ", columnName, begTime, endTime);
        }

        /// <summary>
        /// 转换为左Like语句,如:UserName like ‘%杰‘
        /// </summary>
        /// <param name="columnName">列名</param>
        /// <param name="Value">值</param>
        /// <returns></returns>
        public string ToSQLLeftLike(string columnName, string Value)
        {
            return string.Format(" {0} like ‘%{1}‘", columnName, SQLParameterHandle(Value));
        }

        /// <summary>
        /// 转换为Like语句,如:UserName like ‘%杰%‘
        /// </summary>
        /// <param name="columnName">列名</param>
        /// <param name="Value">值</param>
        /// <returns></returns>
        public string ToSQLLike(string columnName, string Value)
        {
            return string.Format(" {0} like ‘%{1}%‘", columnName, SQLParameterHandle(Value));
        }

        /// <summary>
        /// 转换为右Like语句,如:UserName like ‘杰%‘
        /// </summary>
        /// <param name="columnName">列名</param>
        /// <param name="Value">值</param>
        /// <returns></returns>
        public string ToSQLRightLike(string columnName, string Value)
        {
            return string.Format(" {0} like ‘{1}%‘", columnName, SQLParameterHandle(Value));
        }

        #region To SQL Date

        /// <summary>
        /// 按时间查询的Berween And 语句
        /// </summary>
        /// <param name="columnName">根据查询的列</param>
        /// <param name="beginDate">开始时间</param>
        /// <param name="endDate">结束时间</param>
        /// <returns>String类型的SQL语句</returns>
        public abstract string ToSQLBetweenDate(string columnName, DateTime beginDate, DateTime endDate);

        /// <summary>
        /// 按时间查询的Berween And 语句
        /// </summary>
        /// <param name="columnName">根据查询的列</param>
        /// <param name="beginDate">开始时间</param>
        /// <param name="endDate">结束时间</param>
        /// <returns>String类型的SQL语句</returns>
        public abstract string ToSQLBetweenDateTime(string columnName, DateTime beginDate, DateTime endDate);

        /// <summary>
        /// 转换为SQL语句中的日期类型,格式如:2009-05-18
        /// </summary>
        /// <param name="obj">需要转换为SQL类型的值</param>
        /// <returns></returns>
        public abstract string ToSQLDate(object obj);

        /// <summary>
        /// 转换为SQL语句类型的时间格式,如2010-03-04 12:12:12
        /// </summary>
        /// <param name="obj">需要转换为SQL类型的值</param>
        /// <returns></returns>
        public abstract string ToSQLDateTime(object obj);

        /// <summary>
        /// 转换为当天范围SQL语句
        /// </summary>
        /// <param name="columnName">字段名称</param>
        /// <param name="date">日期</param>
        /// <returns></returns>
        public abstract string ToSQLInduriteDate(string columnName, DateTime date);
        #endregion
    }
}

注意这里,sqlhelp是个工具类,其中因为各个数据库的语法不同,我们部分拼接方法需要重新实现抽象类比如:ToSQLBetweenDate();

SqlServer 写法:

  public override string ToSQLBetweenDate(string FieldName, DateTime beginDate, DateTime endDate)
        {
            return string.Format(" {0} Between Convert(DateTime,‘{1}‘) and Convert(DateTime,‘{2}‘) ", FieldName, beginDate.ToString("yyyy-MM-dd"), endDate.ToString("yyyy-MM-dd"));
        }

Oracle 写法:

    public override string ToSQLBetweenDate(string FieldName, DateTime beginDate, DateTime endDate)
        {
            return string.Format("({0} BETWEEN TO_DATE(‘{1}‘,‘YYYY-MM-DD‘) AND TO_DATE(‘{2}‘,‘YYYY-MM-DD‘))", FieldName, beginDate.ToString("yyyy-MM-dd"), endDate.ToString("yyyy-MM-dd"));
        }

关于DataAccess最重要的职责是负责与数据库通讯,当然这里我就不去讲述是如何实现的了,看了上面那个AddParameter()的讲解之后

里面贴的代码就已经讲述了,如何链接数据库。 而看了我们的SqlHelp之后自然也就能了解我们的“增删查改”是怎么实现了的,

其实很简单.

DataAccessBase :DataAccessCore

DataAccessCore:DataProviderBase

写到这里其实贴的代码多了,有点眼花缭乱的。 我们还是回归到由浅入深的思路去讲解,其实作为使用者到这里就不必过分深究如何实现“增删查改”每一个方法。

接下来我写一下关于DataAccess 另外好用的功能,如果说之前写的事务能Winner框架中最好用的功能,那下面这个功能就可以算的上是第二好用的功能----“分页”

在接触Winner之前,我最常用的是从学校学的那个“分页存储过程”。先不说好用不好用,以动软例总之每个列表要单独处理分页。(也许是我没用好)但是在Winner

里面很简单。贴一下我们是怎么用的:

是的,我们只要设置DA的Changepage这个对象即可。 Winner在控制上用了一个“扩展类”,不清楚扩展类的就要自行百度一下了。

回到前面,可以看到代码生成器生成的集合类操作我们是需要继承 “DataAccessCollectionBase”  而 DataAccessCollectionBase:DataAccessCollectionCore

DataAccessCollectionCore 里我们定义了一个分页对象 ChangePage

ChangePage 实现了 IChangePage 接口,准确来说任何实现了IChangePage的对象都可以使用我们的分页插件。

ChangePage 就是一个分页对象。

/***************************************************
*
* Create Time : 2014/9/1 11:38:37
* Version : V 1.01
* Create User:Jie
* E_Mail : [email protected]
*
***************************************************/
using Winner.Framework.Core.Interface;

namespace Winner.Framework.Core.DataAccess
{
    /// <summary>
    /// 分页控件适配器
    /// </summary>
    public class ChangePage : IChangePage
    {
        /// <summary>
        /// 分页语句类型
        /// </summary>
        public QueryCommandType CommandType { get; set; }

        /// <summary>
        /// 总行数
        /// </summary>
        public int Count { get; set; }

        /// <summary>
        /// 当前页面
        /// </summary>
        public int CurrentPageIndex { get; set; }

        /// <summary>
        /// 每页显示行数
        /// </summary>
        public int PageSize { get; set; }

        /// <summary>
        /// 排序字段名称
        /// </summary>
        public string SortFields { get; set; }

        /// <summary>
        /// 排序顺序
        /// </summary>
        public SortType SortType { get; set; }

        /// <summary>
        /// 构造新的适配器
        /// </summary>
        /// <param name="pageIndex">当前页面码</param>
        /// <param name="pageSize">每页显示行数</param>
        /// <param name="sortFields">排序字段名称</param>
        /// <param name="sortType">排序顺序</param>
        /// <param name="commandType">分页语句类型</param>
        /// <returns></returns>
        public static ChangePage NewInstance(int pageIndex, int pageSize, string sortFields = "", SortType sortType = SortType.DESC, QueryCommandType commandType = QueryCommandType.Text)
        {
            pageIndex = pageIndex <= 0 ? 1 : pageIndex;
            pageSize = pageSize <= 0 ? 10 : pageSize;
            ChangePage changePage = new ChangePage
            {
                CurrentPageIndex = pageIndex,
                PageSize = pageSize,
                SortFields = sortFields,
                SortType = sortType,
                CommandType = commandType,
            };
            return changePage;
        }
    }
}

现在分页需要的基本参数有了,就是在对数据库操作的时候加入这些参数即可:

上面是Sqlserver的实现方式,我们再看一下Oracle 的实现方式:

如果我们想要一页20条数据怎么就和简单了,修改ChangePage的参数就行了。

至于前端,由于我们就用JavaScript去实现了。 这里贴一个Jason写的基于OK写的前端js分页控件:

function pageModel() {
    this.currentPageIndex = 1;
    this.MaxPageIndex = 1;
    this.loadData = function () { };
    this.pagesize = 10;
    this.eleid = "";
}
function koViewModel() {
    var self = this;
    self.Data = [];
}
function renderPage(options) {
    var currentPageIndex = options.pageindex;
    var MaxPageIndex = options.MaxPageIndex;
    var loadData = options.loadData;
    var pagesize = options.pagesize || 10;
    var $this = $("#" + options.eleid).parent();
    var ul = $("<ul></ul>").addClass("pagination");
    var prev = $("<li></li>").append($("<a></a>").append($("<i class=‘icon-double-angle-left‘></i>"))).addClass("prev");
    if (currentPageIndex == 1) {
        prev.addClass("disabled");
    }

    var next = $("<li></li>").append($("<a></a>").append($("<i class=‘icon-double-angle-right‘></i>"))).addClass("next");
    if (MaxPageIndex <= 1 || currentPageIndex == MaxPageIndex) {
        next.addClass("disabled");
    }
    var recordsinfo = $("<li class=‘disabled‘></li>").append($("<a></a>")
        .append("<span>共</span><span class=‘totalrecords‘>" + options.totalRecords + "</span><span>条,每页</span>" +
        "<span class=‘pagesize‘>" + pagesize + "</span>条,<span></span>" + "<span class=‘maxpageindex‘>" + MaxPageIndex + "</span><span>页</span>"));

    var minIndex = currentPageIndex - 5;
    var plus = 0;
    if (minIndex <= 0) {
        plus = 0 - minIndex;
        minIndex = 1;
    }
    var maxIndex = currentPageIndex + 5 + plus;
    if (maxIndex > MaxPageIndex) {
        maxIndex = MaxPageIndex;
    }
    ul.append(prev);
    for (var i = minIndex; i <= maxIndex; i++) {
        var li_html = $("<li></li>");
        if (currentPageIndex == i)
            li_html.addClass("active disabled");
        var item = li_html.append($("<a></a>").html(i));
        ul.append(item);
    }
    ul.append(next);
    ul.append(recordsinfo);
    //ul.append(pagesizeoptions);
    ul.children("li").click(function () {
        if (!$(this).is(".disabled")) {
            var index = 1;
            var pagesize = options.pagesize;
            if ($(this).is(".selectpagesize")) {
                return;
            } else {
                index = $(this).text();
            }
            if (index.length == 0) {
                index = ul.find(".active").text();
                if ($(this).is(".prev") && index > 1) {
                    index--;
                } else if ($(this).is(".next") && index < MaxPageIndex) {
                    index++;
                }
            }
            var arg = $.extend({ "pageindex": parseInt(index), "pagesize": pagesize }, options);
            arg.pageindex = parseInt(index);
            loadData(arg);
        }
    });
    if ($this.parent().find(".dataTables_paginate").length > 0) {
        $this.parent().find(".dataTables_paginate").empty().append(ul);
    } else {
        var div_pagination = $("<div class=\"dataTables_paginate paging_bootstrap\"></div>");
        div_pagination.append(ul);
        $this.after(div_pagination);//.empty().append(ul);
    }
}
function renderLoading(eleid) {
    var loadname = eleid + "-loading-" + new Date().valueOf();
    var $this = $("#" + eleid);
    $this.attr("data-loading-id", loadname);
    var count = $("#" + eleid).find("tr").eq(0).find("td,th").length;
    $this.find("tbody").empty();
    var tr = $("<tr></tr>");
    var td = $("<td></td>").attr("colspan", count);
    var wrap = $("<div></div>").addClass("data-loading-wrap text-center");
    var span = $("<span></span>").css({ "border": "1px solid #aaa", "padding": "5px 10px", "line-height": "16px" });
    var img = $("<img />").attr("src", "/assets/images/ko_loading.gif").addClass("icon");
    var tips = $("<span></span>").html("正在加载数据...");
    span.append(img);
    span.append(tips);
    wrap.append(span);
    td.append(wrap);
    tr.append(td);
    if ($this.get(0).tagName != "TABLE") {
        var table = $("<table></table>").attr("id", loadname);
        table.append(tr);
        $this.append(table);
    } else {
        tr.attr("id", loadname);
        $this.append(tr);
    }
}
function renderWhenNoData(eleid) {
    var loadname = eleid + "-no-data-" + new Date().valueOf();
    var $this = $("#" + eleid);
    $this.attr("data-nodata-id", loadname);
    var count = $("#" + eleid).find("tr").eq(0).find("td,th").length;
    $this.find("tbody").empty();
    var tr = $("<tr></tr>");
    var td = $("<td></td>").attr("colspan", count);
    var wrap = $("<div></div>").addClass("data-loading-wrap text-center");
    var span = $("<span></span>").css({ "border": "1px solid #aaa", "padding": "5px 10px", "line-height": "16px" });
    //var img = $("<img />").attr("src", "/asserts/images/loading.gif").addClass("icon");
    var tips = $("<span></span>").html("暂无数据");
    //span.append(img);
    span.append(tips);
    wrap.append(span);
    td.append(wrap);
    tr.append(td);
    console.log($this.get(0).tagName);
    if ($this.get(0).tagName != "TABLE") {
        var table = $("<table style=‘width:100%;‘></table>").attr("id", loadname).addClass("ajax-nodata");
        table.append(tr);
        $this.append(table);
    } else {
        tr.addClass("ajax-nodata").attr("id", loadname);
        $this.append(tr);
    }
}
(function ($) {
    $.extend({
        DT: {
            init: function (opt) {

                var container = $("#" + opt.eleid).parent().parent();
                var activeIndex = container.find(".dataTables_paginate > .pagination > li.active");
                //console.log(activeIndex);
                var currentPageIndex = activeIndex.length > 0 ? activeIndex.text() : undefined;
                if (opt.pageindex) {

                }
                else if (!opt.pageindex && !currentPageIndex) {
                    opt.pageindex = 1;
                } else {
                    try {
                        opt.pageindex = parseInt(currentPageIndex);
                    } catch (e) {
                        opt.pageindex = 1;
                    }
                }
                if (!opt.pagesize) {
                    opt.pagesize = 10;
                }
                this.loadData(opt);
            },

            loadData: function (opt) {
                var pageindex = opt.pageindex || 1;
                var pagesize = opt.pagesize || 10;
                var url = opt.url || "";
                if (typeof(opt.query) == ‘string‘) {
                    var urlquery = opt.query;
                    opt.query = {};
                    var array = urlquery.split(‘&‘);
                    for (var i in array) {
                        var keyvalue = array[i].split(‘=‘);
                        if (keyvalue[1]) {
                            opt.query[keyvalue[0]] = decodeURI(keyvalue[1]);
                        }
                    }
                }
                var query = opt.query || {};
                var errorfunc = opt.errorfunc || alert;
                var enablePaginate = opt.enablePaginate;
                var searchData = query;
                if (enablePaginate) {
                    searchData = $.extend(query, { "pageindex": pageindex, "pagesize": pagesize });
                }
                renderLoading(opt.eleid);
                var pModel = $.extend(opt, { loadData: this.loadData, MaxPageIndex: 1 });
                $.ajax({
                    "url": url,
                    "data": searchData,
                    "type": "POST",
                    "success": function (resp) {
                        var viewModel = new koViewModel();
                        if (resp.Success) {
                            var loadingid = $("#" + opt.eleid).attr("data-loading-id");
                            $("#" + loadingid).remove();
                            if (resp.Content && resp.Content.Data && resp.Content.Data.length > 0) {

                                viewModel.Data = resp.Content.Data;
                                ko.applyBindings(viewModel, document.getElementById(opt.eleid));
                                if (enablePaginate) {
                                    var maxPageIndex = parseInt(resp.Content.Count / pagesize);
                                    if (resp.Content.Count % pagesize) {
                                        maxPageIndex++;
                                    }
                                    pModel.MaxPageIndex = maxPageIndex;
                                    pModel.totalRecords = resp.Content.Count;
                                    pModel.pagesize = pagesize;
                                    renderPage(pModel);
                                }
                            } else {
                                $(".ajax-nodata").remove();
                                if (opt.renderWhenNoData) {
                                    opt.renderWhenNoData(opt.eleid);
                                } else {
                                    renderWhenNoData(opt.eleid);
                                }
                            }

                            if (opt.success) {
                                opt.success.apply(viewModel, [resp]);
                            }
                        } else {
                            var loadingid = $("#" + opt.eleid).attr("data-loading-id");
                            $("#" + loadingid).remove();
                            errorfunc(resp.Message);
                        }
                    },
                    "error": function () {
                        var loadingid = $("#" + opt.eleid).attr("data-loading-id");
                        $("#" + loadingid).remove();
                        errorfunc("连接服务器失败");
                    }
                })
            }
        }
    });
})(jQuery);

$(function () {
    $(".ko-pager").each(function (index, item) {
        if ($(item).get(0).tagName.toUpperCase() == "INPUT" || $(item).get(0).tagName.toUpperCase() == "BUTTON") {
            $(item).bind("click", function () {
                loadDataByKoAttribute(item);
            });
        } else {
            loadDataByKoAttribute(item);
        }
    });
});
function loadDataByKoAttribute(item) {
    var elementid = $(item).attr("data-ko-element");
    var url = $(item).attr("data-ko-url");
    var queryinfo = eval("(" + $(item).attr("data-ko-query") + ")");
    var enablePaginate = $(item).attr("data-ko-enablepaginate");
    var pagesize = $(item).attr("data-ko-pagesize") || 10;
    var successName = $(item).attr("data-ko-success");
    var successFunction = function () { };
    if (successName) {
        successFunction = eval(successName);
    }
    var query = {};
    for (var key in queryinfo) {
        if ($(queryinfo[key]).length > 1) {
            var value = "";
            $(queryinfo[key]).each(function (i, ele) {
                value += $(ele).val();
                if (i + 1 < $(queryinfo[key]).length) {
                    value += ",";
                }
            });
            query[key] = value;
        } else {
            query[key] = $(queryinfo[key]).val();
        }
    }

    $.DT.init({
        eleid: elementid,
        url: url,
        enablePaginate: enablePaginate == "true" ? true : false,
        pagesize: pagesize,
        query: query,
        success: successFunction
    });
}

引用js后,调用即可。

     function init(pageindex) {
            //异步加载列表参数
            var option = {
                url: ‘@Url.Action("getusers","user")‘,//ajax url
                eleid: ‘user-list‘,//列表的容器ID
                //pagesize:10,//每页数据条数
                enablePaginate: true,//是否开启分页
                //errorfunc:function(str){},//出现错误时的回调
                //success:function(){},//执行成功的回调
                //ajax请求时携带的参数,可以使用query:$("#form1").serialize()
                query: $("#form1").serialize()
            };
            //加载指定页码的数据
            if (pageindex && pageindex > 0) {
                option.pageindex = pageindex;
            }
            //执行分页列表加载
            $.DT.init(option);
        }

好吧,本来想一篇博客把主要的三,四个程序集写完。但是看来一篇博客是写不玩了,只能分成三篇来写。因为里面的东西实在太多了,让总结Winner-DA四大点:

1,多数据的支持;

2,简洁的分页对象;

3,封装事务机制;

4,封装增删查改基本操作;

今天先写到这里,明天继续写。关于代码的问题,今天没忙过来,这几天我会开一个GitHub,然后把这:

Winner.Framework.Core

Winner.Framework.Encrypt

Winner.Framework.Utils

Winner.Framework.MVC

这四个主要的程序集给传上去提供给大家下载。关于项目源码在后期随着文章更新,再不断上传源码。关于Winner框架的讨论可以加群:261083244,或者扫描主页左侧二维码加群。

时间: 2024-09-29 10:42:36

程序员的自我救赎---1.4.1: 核心框架讲解(DAL)的相关文章

一个程序员的自我救赎

为了找一个能清晰表达我在2017年的状态词语,我搜索了不下百个词语还是未能找到一个贴切的形容,可想我这一年的状态该有多么的糟糕.既然无法汇总,只好通过文字来聚焦我的思绪把这混乱一层一层地剥离.观察和思考. 疲惫的身躯 我细数了一下2017到底有多少可以让我"精神抖擞"的日子,算下来可能也就只有那可怕的10天左右,如果用"智能手环"来表达的话,2017我可能只有10天左右的睡眠质量勉强能达到良好.这是一个往我焦虑感火上加油的数字,我觉得这个数字恰恰应该是一个"

程序员的自我救赎---1.4.2: 核心框架讲解(BLL&amp;Tool)

<前言> <目录> (一) Winner2.0 框架基础分析 (二) 短信中心 (三)SSO单点登录 (四)PLSQL报表系统 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八)监控系统 (九)会员中心 (十)消息中心 (十一)Winner前端框架与RPC接口规范讲解 (十二)上层应用案例 (十三)番外篇 <核心框架讲解> 之前想用一篇文章讲完核心框架的三四个程序集,后来写着写着就发现一篇文章写不完,这才想了一下用最少要用三篇. 上一篇讲了一下DAL,其实也没

程序员的自我救赎---1.2:代码生成器的使用

<前言> <目录> (一) Winner2.0 框架基础分析 (二) 短信中心 (三)SSO单点登录 (四)PLSQL报表系统 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八)监控系统 (九)会员中心 (十)消息中心 (十一)Winner前端框架与RPC接口规范讲解 (十二)上层应用案例 (十三)番外篇 <代码生成器的使用> 今天中午阿杰聊了会,阿杰说看了我写的博客后.发现一个问题,把Winner框架整理成文档,要把Winner框架的核心思想 给写出来,比如

程序员的自我救赎---11.3:WinService服务

<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八)监控系统 (九)会员中心 (十) APP版本控制系统 (十一)Winner前端框架与RPC接口规范讲解 (十二)上层应用案例 (十三)总结 <WinService服务> 说道Windows服务基本每个以.net为主要开发语言的技术团队都会用到这个,Winner2.0中对于WinServices也有

程序员的自我救赎---11.4:FileSystem文件服务

<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八)监控系统 (九)会员中心 (十) APP版本控制系统 (十一)Winner前端框架与RPC接口规范讲解 (十二)上层应用案例 (十三)总结 <FileSystem文件服务> 前面写了很多Winner2.0的文章,基本我都是以"首先,开始,最后" 这样的格式体去写,今天换种写法从

程序员的自我救赎---1.1: 解决方案命分层规范

<目录> <Winner2.0框架解决方案命分层规范> 初学编程,难免要从Hello Word开始,学习Winner框架首先要知道如何建一个项目.有了第一个项目的框架结构就知道如何施展自己的"增删查改". Winner框架 依然遵从MVC模式,这里我就不去赘述什么是MVC. 数据层:以"项目名.DataAcces"命名,例如:  Shop.DataAccess: 实体层:以"项目名.Entities" 命名    例如:

程序员的自我救赎---2.1:报表系统项目分析

<前言> <目录> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八)监控系统 (九)会员中心 (十)消息中心 (十一)Winner前端框架与RPC接口规范讲解 (十二)上层应用案例 (十三)番外篇 <报表系统项目分析> “报表系统”顾名思义是用来做报表用的,但是在Winner当中报表系统经常被我们用来当作网页版的PL/SQL来使用. 不用Oralce的

程序员的自我救赎---2.1:短信中心项目分析

<前言> <目录> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八)监控系统 (九)会员中心 (十)消息中心 (十一)Winner前端框架与RPC接口规范讲解 (十二)上层应用案例 (十三)番外篇 <短信中心项目分析> 从本章开始,包括后面的SSO单点登录,钱包系统,GPU支付中心,权限系统,会员中心 都是Jason开发的大作.

程序员的自我救赎---3.1:理解Oauth2.0

<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八)监控系统 (九)会员中心 (十) APP版本控制系统 (十一)Winner前端框架与RPC接口规范讲解 (十二)上层应用案例 (十三)总结 <理解Oauth2.0> 关于SSO分两个篇章来讲,先讲讲Oauth2.0,之前还特地百度了一下Oauth怎么读,我们每次交流的时候都直接读字母O·A·U·T