[转载]OAuth 2.0 - Authorization Code授权方式详解

mikel阅读(1361)

[转载]OAuth 2.0 – Authorization Code授权方式详解 – dotNetDR_ – 博客园.

I:OAuth 2.0 开发前期准备

天上不会自然掉馅饼让你轻松地去访问到人家资源服务器里面的用户数据资源,所以你需要做的前期开发准备工作就是把AppKey, AppSecret取到手 新浪获取传送门腾讯获取传送门

这里说一下,在申请AppKey和AppSecret的过程中,新浪和腾讯的申请做法是有区别的。image
在新浪微博的AppKey,AppSecret申请时会验证你是否拥有域名的所有权

image
而腾讯在这一块上面则没有这个要求!

PS:申请成为开放平台开发者时需要上传身份证电子文件。。。。。

II:为什么不用官方提供的SDK

说到这个我就想吐槽了,这官方的SDK尼玛的明显排斥堆挤咋们做.net的啊!~~~
先上新浪支持的SDK:
image

然后在上腾讯支持的SDK:
image
image

文档资料不全不说,出了问题你还得找人家。所以在这里我也试想过转战JS SDK看看~于是又有了如下的悲剧事情发生:

腾讯和新浪的JS SDK都是主推用js弹窗方面的。这样不太会电脑的用户使用起来的话,就会觉得你的这个第三方应用会不会是病毒神马的。
image IE9下弹窗提示

image Chrome下也会提示,所以这个东西是浏览器本身机制的问题~所以在帖子里面也得到了准确的答复。

image 稍微设置一下允许弹窗的话就得到上面这个怪异摸样。。。

而在这里稍微说一下腾讯的OPENJS这个东西!!我个人感觉它想挑战一下我们开发人员的智商。。。
image这个为什么浏览器没有阻止,完全是在同域的情况下啊~~~TX你这互联老大连另外整个类似于新浪的独立域名的工作都没做好啊!!还在自家的API文档站上高亮标示起这个OpenJS新秀呀。
image

不过相比新浪的JS SDK腾讯自家的OpenJS的技术支持做得非常好的。你只要碰到了问题。都有人在线帮你解答。

PS:如果你选用JS SDK的话,那么你的业务逻辑将会以js脚本的形式暴露在客户端浏览器之下。

III:Authorization Code验证授权模式

基础知识:
在这里先引用前一篇文章里的示例用图,然后再接着讲解各个部分的相关知识。

1.Resource Server(资源服务器):负责存放服务提供商的用户数据资源等相关信息。当第三方应用访问这个资源服务器时,需要提供Access Token否则会提示访问失败。所以我们最终的目的就是让自己开发的第三方Web应用顺利地访问到服务提供商的资源服务器,这才是这个系列文章的最终目 的。

2.Authorization Server(验证授权服务器):负责验证用户账户名密码,以及给第三方WEB应用发放Access Token。在这里我上传两张图片为你叙述Authorization Server是什么样子。
image 新浪的Authorization Server

image
腾讯的Authorization Server

接下来将会继续讲解,这个重要的Access Token(访问令牌)到底是怎么取得的。

首先作为第三方网站上会显示一个跳转到新浪,腾讯授权服务器的<a />超级链接。如下图:
image

下面的图片将介绍这两个链接的跳转地址规范:
image

新浪的规范
https://api.weibo.com/oauth2/authorize?client_id={AppKey}&response_type=code&redirect_uri={YourSiteUrl}

腾讯的规范
https://open.t.qq.com/cgi-bin/oauth2/authorize?client_id={AppKey}&response_type=code&redirect_uri={YourSiteUrl}

可以看出新浪和腾讯的规范在此步骤基本一致。

现在讲述第2步:image

这时Authorization Server将会跳转回申请授权验证的第三方网站~但是会在QueryString内加上一个名为code的参数!例子如下:

腾 讯:http://www.mytestsite.com/Tencent.aspx?code=174256357036c9df7db17342f15a9476&openid=45CD8A7A05A0C3E30D8A9AB74EEAA8D1&openkey=98B2964245A2BE2830F7A793E09FE6B0

新浪:http://www.mytestsite.com/Sina.aspx?code=19b83321705c538e0422ba09ac9043a0

从这一步可以看出企鹅与标准脱离的野心逐渐浮现。。。它不仅仅返回code而且还参杂openid&openkey~不知在各位开发者的眼里会不会觉得比较另类?

当我们拿到跳转回来Url上的QueryString参数code后就可以再次去Authorization Server上请求获取AccessToken这个重要令牌了!下面接着上图!!!
image

具体说一下第3步的请求地址规范:
新浪:https://api.weibo.com/oauth2 /access_token?client_id={AppKey}&client_secret={AppSecret}&grant_type=authorization_code&redirect_uri={YourSiteUrl}&code={code}

腾讯:https://open.t.qq.com/cgi-bin/oauth2 /access_token?client_id={AppKey}&client_secret={AppSecret}&redirect_uri={YourSiteUrl}&grant_type=authorization_code&code={code}

在这一步,腾讯和新浪双方都完全保持一致,非常庆幸!

第4步,重点来了,授权服务器即将返回Access Token。这是以Response Body的方式,所以说Authorization Code授权方式并没有对客户端暴露AccessToken访问令牌。也是我极力推荐使用的一种授权方式。
image
image
上图是新浪返回Access Token的内容

image
image
上图是腾讯返回Access Token的内容

这里需要注意一下第3,4步必须要以http post的方式去发起Request。~

IV:总结

image

下一篇将会讲解如何访问资源服务器!尽请期待吧!

本文到此结束!谢谢。

广告:ASP.NET MVC 3.0 QQ交流群:33353329 (可用于讨论当前的OAuth 2.0)

作者的文章帮助很大
声明: 本文版权归作者dotNetDR_和博客园共有,转载必须保留此段声明。

[转载]MySQL创建index提高多表查询效率

mikel阅读(1120)

[转载]MySQL创建index提高多表查询效率 – 精神邋遢的民工 – 博客园.

本文的目的在于探讨use index for join语句的基本原理。我们知道,在MySQL使用JION语句关联多表查询时,为表创建Index能够大幅度提高查询效率。下面以一个简单的例子来了解其中的原理。

将模型简单化,假设有三个表:tblA, tblB, tblC. 每个表包含三列:col1, col2, col3. 表的其它属性不考虑。

在不创建index的情况下,我们使用以下语句关联三个表:

复制代码
1    SELECT
2       *
3    FROM
4  tblA, 5  tblB, 6  tblC 7    WHERE
8           tblA.col1 = tblB.col1 9       AND tblA.col2 = tblC.col1;
复制代码

对该语句使用EXPLAIN命令查看其处理情况:

复制代码
   +-------+------+---------------+------+---------+------+------+-------------+
   | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
   +-------+------+---------------+------+---------+------+------+-------------+
   | tblA  | ALL  | NULL          | NULL |    NULL | NULL | 1000 |             |
   | tblB  | ALL  | NULL          | NULL |    NULL | NULL | 1000 | Using where |
   | tblC  | ALL  | NULL          | NULL |    NULL | NULL | 1000 | Using where |
   +-------+------+---------------+------+---------+------+------+-------------+
复制代码

关于EXPLAIN命令中,各个参数的具体含义,详解见ali的博文

[http://www.cnitblog.com/aliyiyi08/archive/2008/09/09/48878.html]

查询机制 

对于命令的查询机制,可以参照下MySQL manual(7.2.1)中的一段说明:

The tables are listed in the output in the order that MySQL would read them while processing the query. MySQL resolves all joins using a single-sweep multi-join method. This means that MySQL reads a row from the first table, then finds a matching row in the second table, then in the third table, and so on. When all tables are processed, MySQL outputs the selected columns and backtracks through the table list until a table is found for which there are more matching rows. The next row is read from this table and the process continues with the next table.

正如上面所说,MySQL按照tblA, tblB, tblC的顺序依次读取数据,从EXPLAIN输出的信息结构看,之前的表查询的数据用来查找当前表对应的内容。即用tblA的值来查找tblB中满足条件的值,tblB的值用来查找tblC中满足条件的值。而当一次查找完成时(即三个表的值都查找过一次),MySQL并不会重新返回到tblA中的下一个数据重新开始,而是继续返回到tblB中的数据,来看tblB中是否还有其它行的值和tblA相匹配,如果有的话,继续到tblC,重复刚才的过程。这整个过程的关键原则就是:使用前一个表查询的数据来查找当前表对应的内容。

了解到MySQL在执行多表查询时使用前一个表查询的数据来查找当前表对应的内容这一原理后,那么创建Index的目的就是告诉MySQL怎么去直接找到下一个表的对应数据,如何按照MySQL需要的数据顺序来关联(JOIN)一个表。

再拿刚才的例子,tblAtblB两个表通过条件 ”tblA.col1 = tblB.col1” 关联起来。我们首先获得tblA.col1,接下来MySQL需要的是来自tblB.col1的值,所以我们为它创建INDEX tblB.col1. 创建index后再次EXPLAIN之前的查询命令如下:

复制代码
   +-------+------+---------------+----------+---------+-----------+------+-------------+
   | table | type | possible_keys | key      | key_len | ref       | rows | Extra       |
   +-------+------+---------------+----------+---------+-----------+------+-------------+
   | tblA  | ALL  | NULL          | NULL     |    NULL | NULL      | 1000 |             |
   | tblB  | ref  | ndx_col1      | ndx_col1 |       5 | tblA.col1 |    1 | Using where |
   | tblC  | ALL  | NULL          | NULL     |    NULL | NULL      | 1000 | Using where |
   +-------+------+---------------+----------+---------+-----------+------+-------------+
复制代码

从结果中可以看出,MySQL现在使用key ‘ndx_col1’来关联tblBtblA。也就是说,当MySQL查找tblB中的各行数据时,它直接使用key ‘ndx_col1’ 对应的tblA.col1来找到对应的行,而不是像之前那样进行全表扫描查找。

例子

举一个实例说明用法

其中USING选择的参数,要求是每个表所共有且在每个表中值不重复,以保证index唯一。

join (PRIMARY)中PRIMARY参数为Index名,

表的属性中,作为index需要将参数勾选PK属性,即Primary Key

勾选telnum作为主键,需要将Default值中默认的NULL删除,PRIMARY_KEY不允许包含NULL

为每一个表创建了Index值后,EXPLAIN输出为:

对于MySQL,不管多复杂的查询,每次只需要按照EXPLAIN显示的顺序关联两张表中的内容。创建Index是为了让MySQL能够利用已经查找到的内容来快速找到下一张表的对应行内容。

参考:[http://hackmysql.com/case4] [How To Index For Joins With MySQL]

[转载]java通过jna调用科大讯飞语音云实现语音识别功能

mikel阅读(1248)

[转载]java通过jna调用科大讯飞语音云实现语音识别功能 – 夜明的孤行灯 – 博客园.

语音识别技术

语音识别就是让机器通过识别和理解过程把语音信号转变为相应的文本或命令。语音识别技术主要包括特征提取技术、模式匹配准则及模型训练技术三个方 面。说实话其中的技术比较多,要独立开发新的基本上不现实。所以自然把目光放到开源项目或者其他公司的API上面了。开源项目我尝试了 SpeakRight和sphinx4,但是效果都是一般。AT&T的API老是申请不上,最后把目光放在科大讯飞上了。试用了一下,效果还行, 但是它提供的Windows平台的API是C/C++的,我只懂点皮毛,所以稍微研究了一下通过Java调用它的语音云SDK。

JNA

java调用.dll获取.so一般通过JNI,但是JNI的使用比较复杂,需要用C另写一个共享库进行适配。而JNA是一个自动适配工具,通过它调用.dll只需要一个借口即可。

官网:https://github.com/twall/jna/

下载jna.jar即可。

编写接口

科大讯飞语音云主要提供语音合成和语音识别两个方面的东西,我主要使用语音识别这块的功能。

建立接口QTSR,继承Library。

将msc.dll等文件复制到项目根目录。

jna-yuyinshibie-01

加载msc.dll

QTSR INSTANCE = (QTSR) Native.loadLibrary("msc", QTSR.class);

然后来看一下msc.dll公开了哪些方法。首先是QISRInit,这是一个全局初始化函数。

jna-yuyinshibie-02

它的返回值为int,参数是const char*。int还是java的int,但是char*就对应的是java的String了。

所以在QTSR中添加方法:

public int QISRInit(String configs);

返回值在msp_errors.h中定义,等一下我们还是要弄在java里面去。

继续看QISRInit函数,在官方文档中有调用示例:

const char* configs=“server_url=dev.voicecloud.cn, timeout=10000, vad_enable=true”;
int   ret = QISRInit( configs );
if(MSP_SUCCESS != ret )
{
        printf( “QISRInit failed, error code is: %d”, ret );
}

 

 

对应的在java中的调用代码如下:

String config = "server_url=dev.voicecloud.cn, timeout=10000, vad_enable=true";
int code = QTSR.INSTANCE.QISRInit(config);
if (code != 0) {
  System.out.println("QISRInit failed, error code is:" + code);
}

我们在看一个函数:QISRSessionBegin,这个开始一路ISR会话。

jna-yuyinshibie-03

还是刚才的思路,char*对应java的String,但是注意一下int *errorCode。这个函数其实传入两个参数,传出两个参数。即本身返回的sessionId,还有errorCode。

这里的int*对应的是jna的IntByReference。所以添加方法:

public String QISRSessionBegin(String grammarList, String params,IntByReference errorCode);

 

同样看看官方示例:

/* vad_timeout和vad_speech_tail两个参数只有在打开VAD功能时才生效 */
const char*      params=
“ssm=1,sub=iat,aue=speex-wb;7,auf=audio/L16;rate=16000,ent=sms16k,rst=plain,vad_timeout=1000,vad_speech_tail=1000”;
int ret = MSP_SUCCESS;
const char*     session_id = QISRSessionBegin( NULL, params, &ret );
if(MSP_SUCCESS != ret )
{
        printf( “QISRSessionBegin failed, error code is: %d”, ret );
}

在java这样写:

String params = "ssm=1,sub=iat,aue=speex-wb;7,auf=audio/L16;rate=16000,ent=sms16k,rst=plain,vad_timeout=1000,vad_speech_tail=1000";
IntByReference errorCode = new IntByReference();
String sessionId = QTSR.INSTANCE.QISRSessionBegin(null, params,errorCode);

运行效果:

jna-yuyinshibie-04

其他的函数处理方式大致相同,这里贴上一个c和java在jna中的类型对应表:

Native Type Java Type
char byte
short short
wchar_t char
int int
int boolean
long NativeLong
long long long
float float
double double
char* String
void* Pointer

其中Unsigned类型和signed在java中对应是一样的。

.h文件和常量处理

在SDK的include目录有4个.h文件,定义了一些常量,比如上面一节中的0其实是msp_errors.h中MSP_SUCCESS。

我以msp_errors.h为例,建立一个接口Msp_errors,继承StdCallLibrary。

照着msp_errors.h中的定义在Msp_errors中进行定义。

public static final int MSP_SUCCESS = 0;
public static final int ERROR_FAIL  = -1;
public static final int ERROR_EXCEPTION= -2;
public static final int ERROR_GENERAL= 10100;  
public static final int ERROR_OUT_OF_MEMORY= 10101;    
public static final int ERROR_FILE_NOT_FOUND= 10102;   
public static final int ERROR_NOT_SUPPORT= 10103;  

使用很简单的,比如MSP_SUCCESS 就是Msp_errors.MSP_SUCCESS。

完整代码和文件

这个只是语音识别部分的,语音合成的话我记得有人做过jni接口的。

*QTSR.java

package com.cnblogs.htynkn;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
/**
 * @author 夜明的孤行灯
 * @date 2012-7-5
 */
public interface QTSR extends Library {
    QTSR INSTANCE = (QTSR) Native.loadLibrary("msc", QTSR.class);
    /**
     * 初始化MSC的ISR部分
     *
     * @param configs
     *            初始化时传入的字符串,以指定合成用到的一些配置参数,各个参数以“参数名=参数值”的形式出现,大小写不敏感,不同的参数之间以“
     *            ,”或“\n”隔开,不设置任何值时可以传入NULL或空串:
     * @return 如果函数调用成功返回MSP_SUCCESS,否则返回错误代码,错误代码参见msp_errors
     */
    public int QISRInit(String configs);
    /**
     * 开始一个ISR会话
     *
     * @param grammarList
     *            uri-list格式的语法,可以是一个语法文件的URL或者一个引擎内置语法列表。可以同时指定多个语法,不同的语法之间以“,”
     *            隔开。进行语音听写时不需要语法,此参数设定为NULL或空串即可;进行语音识别时则需要语法,语法可以在此参数中指定,
     *            也可以随后调用QISRGrammarActivate指定识别所用的语法。
     * @param params
     *            本路ISR会话使用的参数,可设置的参数及其取值范围请参考《可设置参数列表_MSP20.xls》,各个参数以“参数名=参数值”
     *            的形式出现,不同的参数之间以“,”或者“\n”隔开。
     * @param errorCode
     *            如果函数调用成功则其值为MSP_SUCCESS,否则返回错误代码,错误代码参见msp_errors。几个主要的返回值:
     *            MSP_ERROR_NOT_INIT 未初始化 MSP_ERROR_INVALID_PARA 无效的参数
     *            MSP_ERROR_NO_LICENSE 开始一路会话失败
     * @return MSC为本路会话建立的ID,用来唯一的标识本路会话,供以后调用其他函数时使用。函数调用失败则会返回NULL。
     */
    public String QISRSessionBegin(String grammarList, String params,
            IntByReference errorCode);
    /**
     * 传入语法
     *
     * @param sessionID
     *            由QISRSessionBegin返回过来的会话ID。
     * @param grammar
     *            语法字符串
     * @param type
     *            语法类型,可以是uri-list、abnf、xml等
     * @param weight
     *            本次传入语法的权重,本参数在MSP 2.0中会被忽略。
     * @return 如果函数调用成功返回MSP_SUCCESS,否则返回错误代码,错误代码参见msp_errors
     */
    public int QISRGrammarActivate(String sessionID, String grammar,
            String type, int weight);
    /**
     * 写入用来识别的语音
     *
     * @param sessionID
     *            由QISRSessionBegin返回过来的会话ID。
     * @param waveData
     *            音频数据缓冲区起始地址
     * @param waveLen
     *            音频数据长度,其大小不能超过设定的max_audio_size
     * @param audioStatus
     *            用来指明用户本次识别的音频是否发送完毕,可能值如下:
     *            MSP_AUDIO_SAMPLE_FIRST = 1 第一块音频
     *            MSP_AUDIO_SAMPLE_CONTINUE = 2 还有后继音频
     *            MSP_AUDIO_SAMPLE_LAST = 4 最后一块音频
     * @param epStatus
     *            端点检测(End-point detected)器所处的状态,可能的值如下:
     *            MSP _EP_LOOKING_FOR_SPEECH = 0 还没有检测到音频的前端点。
     *            MSP _EP_IN_SPEECH = 1 已经检测到了音频前端点,正在进行正常的音频处理。
     *            MSP _EP_AFTER_SPEECH = 3 检测到音频的后端点,后继的音频会被MSC忽略。
     *            MSP _EP_TIMEOUT = 4 超时。
     *            MSP _EP_ERROR= 5 出现错误。
     *            MSP _EP_MAX_SPEECH = 6 音频过大。
     * @param recogStatus
     *            识别器所处的状态
     * @return
     */
    public int QISRAudioWrite(String sessionID, Pointer waveData, int waveLen,
            int audioStatus, IntByReference epStatus, IntByReference recogStatus);
    /**
     * 获取识别结果
     *
     * @param sessionID 由QISRSessionBegin返回过来的会话ID。
     * @param rsltStatus 识别结果的状态,其取值范围和含义请参考QISRAudioWrite的参数recogStatus
     * @param waitTime 与服务器交互的间隔时间,可以控制和服务器的交互频度。单位为ms,建议取值为5000。
     * @param errorCode 如果函数调用成功返回MSP_SUCCESS,否则返回错误代码,错误代码参见msp_errors
     * @return 函数执行成功并且获取到识别结果时返回识别结果,函数执行成功没有获取到识别结果时返回NULL
     */
    public String QISRGetResult(String sessionID, IntByReference rsltStatus,
            int waitTime, IntByReference errorCode);
    /**
     * 结束一路会话
     *
     * @param sessionID 由QISRSessionBegin返回过来的会话ID。
     * @param hints 结束本次会话的原因描述,用于记录日志,便于用户查阅或者跟踪某些问题。
     * @return
     */
    public int QISRSessionEnd(String sessionID, String hints);
    /**
     * 获取与识别交互相关的参数
     *
     * @param sessionID 由QISRSessionBegin返回过来的会话ID。
     * @param paramName 要获取的参数名称;支持同时查询多个参数,查询多个参数时,参数名称按“,” 或“\n”分隔开来。
     * @param paramValue 获取的参数值,以字符串形式返回;查询多个参数时,参数值之间以“;”分开,不支持的参数将返回空的值。
     * @param valueLen 参数值的长度。
     * @return
     */
    public int QISRGetParam(String sessionID, String paramName,
            String paramValue, IntByReference valueLen);
    /**
     * 逆初始化MSC的ISR部分
     *
     * @return
     */
    public int QISRFini();
}

*Msp_errors

package com.cnblogs.htynkn;
import com.sun.jna.win32.StdCallLibrary;
/**
 * @author 夜明的孤行灯
 * @date 2012-7-5
 */
public interface Msp_errors extends StdCallLibrary {
    public static final int MSP_SUCCESS = 0;
    public static final int ERROR_FAIL  = -1;
    public static final int ERROR_EXCEPTION= -2;
    public static final int ERROR_GENERAL= 10100;   /* 0x2774 */
    public static final int ERROR_OUT_OF_MEMORY= 10101;     /* 0x2775 */
    public static final int ERROR_FILE_NOT_FOUND= 10102;    /* 0x2776 */
    public static final int ERROR_NOT_SUPPORT= 10103;   /* 0x2777 */
    public static final int ERROR_NOT_IMPLEMENT= 10104;     /* 0x2778 */
    public static final int ERROR_ACCESS= 10105;    /* 0x2779 */
    public static final int ERROR_INVALID_PARA= 10106;  /* 0x277A */
    public static final int ERROR_INVALID_PARA_VALUE= 10107;    /* 0x277B */
    public static final int ERROR_INVALID_HANDLE= 10108;    /* 0x277C */
    public static final int ERROR_INVALID_DATA= 10109;  /* 0x277D */
    public static final int ERROR_NO_LICENSE= 10110;    /* 0x277E */
    public static final int ERROR_NOT_INIT= 10111;  /* 0x277F */
    public static final int ERROR_NULL_HANDLE= 10112;   /* 0x2780 */
    public static final int ERROR_OVERFLOW= 10113;  /* 0x2781 */
    public static final int ERROR_TIME_OUT= 10114;  /* 0x2782 */
    public static final int ERROR_OPEN_FILE= 10115;     /* 0x2783 */
    public static final int ERROR_NOT_FOUND= 10116;     /* 0x2784 */
    public static final int ERROR_NO_ENOUGH_BUFFER= 10117;  /* 0x2785 */
    public static final int ERROR_NO_DATA= 10118;   /* 0x2786 */
    public static final int ERROR_NO_MORE_DATA= 10119;  /* 0x2787 */
    public static final int ERROR_NO_RESPONSE_DATA= 10120;  /* 0x2788 */
    public static final int ERROR_ALREADY_EXIST= 10121;     /* 0x2789 */
    public static final int ERROR_LOAD_MODULE= 10122;   /* 0x278A */
    public static final int ERROR_BUSY  = 10123;    /* 0x278B */
    public static final int ERROR_INVALID_CONFIG= 10124;    /* 0x278C */
    public static final int ERROR_VERSION_CHECK= 10125;     /* 0x278D */
    public static final int ERROR_CANCELED= 10126;  /* 0x278E */
    public static final int ERROR_INVALID_MEDIA_TYPE= 10127;    /* 0x278F */
    public static final int ERROR_CONFIG_INITIALIZE= 10128;     /* 0x2790 */
    public static final int ERROR_CREATE_HANDLE= 10129;     /* 0x2791 */
    public static final int ERROR_CODING_LIB_NOT_LOAD= 10130;   /* 0x2792 */
/* Error codes of network 10200(0x27D8)*/
    public static final int ERROR_NET_GENERAL= 10200;   /* 0x27D8 */
    public static final int ERROR_NET_OPENSOCK= 10201;  /* 0x27D9 */   /* Open socket */
    public static final int ERROR_NET_CONNECTSOCK= 10202;   /* 0x27DA */   /* Connect socket */
    public static final int ERROR_NET_ACCEPTSOCK = 10203;   /* 0x27DB */   /* Accept socket */
    public static final int ERROR_NET_SENDSOCK= 10204;  /* 0x27DC */   /* Send socket data */
    public static final int ERROR_NET_RECVSOCK= 10205;  /* 0x27DD */   /* Recv socket data */
    public static final int ERROR_NET_INVALIDSOCK= 10206;   /* 0x27DE */   /* Invalid socket handle */
    public static final int ERROR_NET_BADADDRESS = 10207;   /* 0x27EF */   /* Bad network address */
    public static final int ERROR_NET_BINDSEQUENCE= 10208;  /* 0x27E0 */   /* Bind after listen/connect */
    public static final int ERROR_NET_NOTOPENSOCK= 10209;   /* 0x27E1 */   /* Socket is not opened */
    public static final int ERROR_NET_NOTBIND= 10210;   /* 0x27E2 */   /* Socket is not bind to an address */
    public static final int ERROR_NET_NOTLISTEN  = 10211;   /* 0x27E3 */   /* Socket is not listening */
    public static final int ERROR_NET_CONNECTCLOSE= 10212;  /* 0x27E4 */   /* The other side of connection is closed */
    public static final int ERROR_NET_NOTDGRAMSOCK= 10213;  /* 0x27E5 */   /* The socket is not datagram type */
    public static final int ERROR_NET_DNS= 10214;   /* 0x27E6 */   /* domain name is invalid or dns server does not function well */
/* Error codes of mssp message 10300(0x283C) */
    public static final int ERROR_MSG_GENERAL= 10300;   /* 0x283C */
    public static final int ERROR_MSG_PARSE_ERROR= 10301;   /* 0x283D */
    public static final int ERROR_MSG_BUILD_ERROR= 10302;   /* 0x283E */
    public static final int ERROR_MSG_PARAM_ERROR= 10303;   /* 0x283F */
    public static final int ERROR_MSG_CONTENT_EMPTY= 10304;     /* 0x2840 */
    public static final int ERROR_MSG_INVALID_CONTENT_TYPE      = 10305;    /* 0x2841 */
    public static final int ERROR_MSG_INVALID_CONTENT_LENGTH    = 10306;    /* 0x2842 */
    public static final int ERROR_MSG_INVALID_CONTENT_ENCODE    = 10307;    /* 0x2843 */
    public static final int ERROR_MSG_INVALID_KEY= 10308;   /* 0x2844 */
    public static final int ERROR_MSG_KEY_EMPTY= 10309;     /* 0x2845 */
    public static final int ERROR_MSG_SESSION_ID_EMPTY= 10310;  /* 0x2846 */
    public static final int ERROR_MSG_LOGIN_ID_EMPTY= 10311;    /* 0x2847 */
    public static final int ERROR_MSG_SYNC_ID_EMPTY= 10312;     /* 0x2848 */
    public static final int ERROR_MSG_APP_ID_EMPTY= 10313;  /* 0x2849 */
    public static final int ERROR_MSG_EXTERN_ID_EMPTY= 10314;   /* 0x284A */
    public static final int ERROR_MSG_INVALID_CMD= 10315;   /* 0x284B */
    public static final int ERROR_MSG_INVALID_SUBJECT= 10316;   /* 0x284C */
    public static final int ERROR_MSG_INVALID_VERSION= 10317;   /* 0x284D */
    public static final int ERROR_MSG_NO_CMD= 10318;    /* 0x284E */
    public static final int ERROR_MSG_NO_SUBJECT= 10319;    /* 0x284F */
    public static final int ERROR_MSG_NO_VERSION= 10320;    /* 0x2850 */
    public static final int ERROR_MSG_MSSP_EMPTY= 10321;    /* 0x2851 */
    public static final int ERROR_MSG_NEW_RESPONSE= 10322;  /* 0x2852 */
    public static final int ERROR_MSG_NEW_CONTENT= 10323;   /* 0x2853 */
    public static final int ERROR_MSG_INVALID_SESSION_ID        = 10324;    /* 0x2854 */
/* Error codes of DataBase 10400(0x28A0)*/
    public static final int ERROR_DB_GENERAL= 10400;    /* 0x28A0 */
    public static final int ERROR_DB_EXCEPTION= 10401;  /* 0x28A1 */
    public static final int ERROR_DB_NO_RESULT= 10402;  /* 0x28A2 */
    public static final int ERROR_DB_INVALID_USER= 10403;   /* 0x28A3 */
    public static final int ERROR_DB_INVALID_PWD= 10404;    /* 0x28A4 */
    public static final int ERROR_DB_CONNECT= 10405;    /* 0x28A5 */
    public static final int ERROR_DB_INVALID_SQL= 10406;    /* 0x28A6 */
    public static final int ERROR_DB_INVALID_APPID= 10407;  /* 0x28A7 */
/* Error codes of Resource 10500(0x2904)*/
    public static final int ERROR_RES_GENERAL= 10500;   /* 0x2904 */
    public static final int ERROR_RES_LOAD = 10501;     /* 0x2905 */   /* Load resource */
    public static final int ERROR_RES_FREE = 10502;     /* 0x2906 */   /* Free resource */
    public static final int ERROR_RES_MISSING = 10503;  /* 0x2907 */   /* Resource File Missing */
    public static final int ERROR_RES_INVALID_NAME  = 10504;    /* 0x2908 */   /* Invalid resource file name */
    public static final int ERROR_RES_INVALID_ID    = 10505;    /* 0x2909 */   /* Invalid resource ID */
    public static final int ERROR_RES_INVALID_IMG   = 10506;    /* 0x290A */   /* Invalid resource image pointer */
    public static final int ERROR_RES_WRITE= 10507;     /* 0x290B */   /* Write read-only resource */
    public static final int ERROR_RES_LEAK = 10508;     /* 0x290C */   /* Resource leak out */
    public static final int ERROR_RES_HEAD = 10509;     /* 0x290D */   /* Resource head currupt */
    public static final int ERROR_RES_DATA = 10510;     /* 0x290E */   /* Resource data currupt */
    public static final int ERROR_RES_SKIP = 10511;     /* 0x290F */   /* Resource file skipped */
/* Error codes of TTS 10600(0x2968)*/
    public static final int ERROR_TTS_GENERAL= 10600;   /* 0x2968 */
    public static final int ERROR_TTS_TEXTEND = 10601;  /* 0x2969 */  /* Meet text end */
    public static final int ERROR_TTS_TEXT_EMPTY= 10602;    /* 0x296A */  /* no synth text */
/* Error codes of Recognizer 10700(0x29CC) */
    public static final int ERROR_REC_GENERAL= 10700;   /* 0x29CC */
    public static final int ERROR_REC_INACTIVE= 10701;  /* 0x29CD */
    public static final int ERROR_REC_GRAMMAR_ERROR= 10702;     /* 0x29CE */
    public static final int ERROR_REC_NO_ACTIVE_GRAMMARS        = 10703;    /* 0x29CF */
    public static final int ERROR_REC_DUPLICATE_GRAMMAR= 10704;     /* 0x29D0 */
    public static final int ERROR_REC_INVALID_MEDIA_TYPE        = 10705;    /* 0x29D1 */
    public static final int ERROR_REC_INVALID_LANGUAGE= 10706;  /* 0x29D2 */
    public static final int ERROR_REC_URI_NOT_FOUND= 10707;     /* 0x29D3 */
    public static final int ERROR_REC_URI_TIMEOUT= 10708;   /* 0x29D4 */
    public static final int ERROR_REC_URI_FETCH_ERROR= 10709;   /* 0x29D5 */
/* Error codes of Speech Detector 10800(0x2A30) */
    public static final int ERROR_EP_GENERAL= 10800;    /* 0x2A30 */
    public static final int ERROR_EP_NO_SESSION_NAME= 10801;    /* 0x2A31 */
    public static final int ERROR_EP_INACTIVE  = 10802;     /* 0x2A32 */
    public static final int ERROR_EP_INITIALIZED    = 10803;    /* 0x2A33 */
/* Error codes of TUV */ 
    public static final int ERROR_TUV_GENERAL= 10900;   /* 0x2A94 */
    public static final int ERROR_TUV_GETHIDPARAM       = 10901;    /* 0x2A95 */   /* Get Busin Param huanid*/
    public static final int ERROR_TUV_TOKEN= 10902;     /* 0x2A96 */   /* Get Token */
    public static final int ERROR_TUV_CFGFILE= 10903;   /* 0x2A97 */   /* Open cfg file */
    public static final int ERROR_TUV_RECV_CONTENT  = 10904;    /* 0x2A98 */   /* received content is error */
    public static final int ERROR_TUV_VERFAIL    = 10905;   /* 0x2A99 */   /* Verify failure */
/* Error codes of IMTV */
    public static final int ERROR_LOGIN_SUCCESS= 11000;     /* 0x2AF8 */   /* 成功 */
    public static final int ERROR_LOGIN_NO_LICENSE          = 11001;    /* 0x2AF9 */   /* 试用次数结束,用户需要付费 */
    public static final int ERROR_LOGIN_SESSIONID_INVALID       = 11002;    /* 0x2AFA */   /* SessionId失效,需要重新登录通行证 */
    public static final int ERROR_LOGIN_SESSIONID_ERROR= 11003;     /* 0x2AFB */   /* SessionId为空,或者非法 */
    public static final int ERROR_LOGIN_UNLOGIN       = 11004;  /* 0x2AFC */   /* 未登录通行证 */
    public static final int ERROR_LOGIN_INVALID_USER            = 11005;    /* 0x2AFD */   /* 用户ID无效 */
    public static final int ERROR_LOGIN_INVALID_PWD             = 11006;    /* 0x2AFE */   /* 用户密码无效 */
    public static final int ERROR_LOGIN_SYSTEM_ERROR= 11099;    /* 0x2B5B */   /* 系统错误 */
/* Error codes of HCR */
    public static final int ERROR_HCR_GENERAL= 11100;
    public static final int ERROR_HCR_RESOURCE_NOT_EXIST        = 11101;
    public static final int ERROR_HCR_CREATE= 11102;
    public static final int ERROR_HCR_DESTROY= 11103;
    public static final int ERROR_HCR_START= 11104;
    public static final int ERROR_HCR_APPEND_STROKES= 11105;
    public static final int ERROR_HCR_GET_RESULT= 11106;
    public static final int ERROR_HCR_SET_PREDICT_DATA= 11107;
    public static final int ERROR_HCR_GET_PREDICT_RESULT        = 11108;
/* Error codes of http 12000(0x2EE0) */
    public static final int ERROR_HTTP_BASE= 12000; /* 0x2EE0 */
/*Error codes of ISV */
    public static final int ERROR_ISV_NO_USER  = 13000; /* 32C8 */    /* the user doesn't exist */
/* Error codes of Lua scripts */
    public static final int ERROR_LUA_BASE= 14000;    /* 0x36B0 */
    public static final int ERROR_LUA_YIELD= 14001; /* 0x36B1 */
    public static final int ERROR_LUA_ERRRUN= 14002;    /* 0x36B2 */
    public static final int ERROR_LUA_ERRSYNTAX= 14003; /* 0x36B3 */
    public static final int ERROR_LUA_ERRMEM= 14004;    /* 0x36B4 */
    public static final int ERROR_LUA_ERRERR= 14005;    /* 0x36B5 */
}

嫌复制粘贴麻烦的可以点击下面的链接下载:

Msp_errors.java: http://www.t00y.com/file/8170772
QTSR.java: http://www.t00y.com/file/8170775

[转载]Windows Phone 开发的资源整理

mikel阅读(948)

[转载]Windows Phone 开发的资源整理 – 贝壳笨 – 博客园.

基础入门级别31天了解: http://www.jeffblankenburg.com/category/31-days-of-mango/  建议关注该作者

 

开源工具 Windows Phone Silverlight 工具包, Coding4Fun Windows Phone工具包, Sterling DB, GZipWebClient , 粒子系统 http://microstar.codeplex.com/  基本都在 codeplex.com

 

关注 http://windowsphonegeek.com/ Telerik, ComponentOneInfragistics网站

问题和解答 StackOverflow

 

控件 http://www.componentone.com/SuperProducts/StudioWindowsPhone/

WP7Contrib 这个工具打包了很多很好的组件和工具包括UI控件,设计模式,转换特效等等。这篇文章提到的一些工具也在这个工具包中。

GridHelper 我最近才发现这个工具。它可以帮助你根据Metro的设计风格放置界面元素

LowProfileImageLoaderDavid Anson写了几个非常棒的类,其中一个就是扩展的Image控件,主要用于图片源是在云端的情况。这个控件会在后台下载图片而不影响前台的线程,特别适合一个页面有很多图片要显示的情况

Placeholder imagesDavid Anson最近发布了一个WPF/SL/WP7控件,用来在图片找不到,或者装载慢的时候显示占位图片(placeholder images)。

 

 

Metro界面外观 http://themetrodevelopershow.com/ , http://ux.artu.tv/?page_id=190 

Metro UI界面完全解析 http://mobile.csdn.net/a/20120117/310896.html

设计相关文章 http://www.alibuybuy.com/posts/69727.html http://www.wpdang.com/archives/3610.html

MVVM模式:MVVM Light,UltraLight MVVM以及Caliburn Micro。  基础 http://msdn.microsoft.com/zh-cn/library/cc278072(v=VS.95).aspx

 

Theme Resources http://msdn.microsoft.com/en-us/library/ff769552%28VS.92%29.aspx

 

Isolated Storage http://isostorespy.codeplex.com/

 

LINQ之路系列博客导航 http://www.cnblogs.com/lifepoem/archive/2011/12/16/2288017.html

本地数据库 ◆SQL CE http://www.cnblogs.com/zjypp/archive/2012/02/29/2373614.html http://windowsphonegeek.com/articles/Windows-Phone-Mango-Local-Database-mapping-and-database-operations http://windowsphonegeek.com/articles/Windows-Phone-Mango-Local-Database-mapping-and-database-operations

 

Live API: SkyDrive与Live服务套件之间的交互性更上一层楼。点击此处查看更多详情,这里还有另一篇文章

Windows Azure: 点击此处了解关于Windows Azure 另外,各位只要订阅MSDN,就能够第一时间免费使用Azure的强大功能,点击此处了解更多内容。

Azure WP7 工具包http://watwp.codeplex.com/ 教程:http://www.wadewegner.com/2011/11/nuget-packages-for-windows-azure-and-windows-phone-developers/

 

欢迎大家补充!多谢!

[转载]java代码分析及分析工具

mikel阅读(990)

[转载]java代码分析及分析工具 – yanbin_new – 博客园.

一个项目从搭建开始,开发的初期往往思路比较清晰,代码也比较清晰。随着时间的推移,业务越来越复杂。代码也就面临着耦合,冗余,甚至杂乱,到最后谁都不敢碰。

作为一个互联电子商务网站的业务支撑系统,业务复杂不言而喻。从09年开始一直沿用到现在,中间代码经过了多少人的手,留下了多少的坑,已经记不清楚了,谁也说不清了。

代码的维护成本越来越高。代码已经急需做调整和改善。最近项目组专门设立了一个小组,利用业余时间做代码分析的工作,目标对核心代码进行分析并进行设计重构。

  代码分析如果用人工来做,需要两点:1、开发人员代码造诣要求很高。2、开发人员投入时间成本非常大。

然而现在网络上 Java代码分析工具做的比较好。所以考虑开始利用这些工具对代码进行分析,并做修改。当然最好在最后有个资深人士做相关的review或开发人员自检。

先考虑工具吧,工欲善其事,必先利其器。所以决定search一下,代码分析和代码分析的工具,便于更好的利用和进行。

  学习一下并做一些归纳:

      提到静态代码的概念:个人理解为 不需要运行起来的代码所关注的点。就是就代码看代码,语法、结构、接口、类等。

    整个软件开发生命周期中,网上说30% 至 70% (占有量还是很大的)的代码逻辑设计和编码缺陷是可以通过静态代码分析来发现和修复的。看来效果还是很明显的。

  静态代码分析的好处,的确有很多好处:

1. 帮助程序开发人员自动执行静态代码分析,快速定位代码隐藏错误和缺陷。

2. 帮助代码设计人员更专注于分析和解决代码设计缺陷。

3. 显著减少在代码逐行检查上花费的时间,提高软件可靠性并节省软件开发和测试成本

概念不多提了,看工具:

1、findbugs:

  安装:http://sourceforge.net/projects/findbugs/

看使用体验:针对一个项目启动findbugs,会进入findbugs的工作视图

 

 如上图

  蓝色区域:罗列出了findbugs在代码中检测到的代码问题。同样的红色为严重的,黄色为警告。

       由于自己写的学习项目所以代码不庞大,质量也还行,所以检测出来的都是黄色警告的。

  紫色区域:点击对应的检测点,能定位到问题代码具体的位置,并且在左边有个bug虫子给出具体问题的信息

  绿色区域:对应左边蓝色区域的检测点,给出的详细描述和建议。

     个人觉得findbugs对于代码检测的深度力度要更好。比如,子类名和父类名相同,死存储(没有调用却开辟空间的),一个方法没有对参数对象验证空等等。

  对代码的质量检查做的很到位。而且很清晰。当然findbugs 也有自定义的设置,个人觉得现有功能已经很好用了,不需要自定义。

2、Checkstyle :

安装:http://sourceforge.net/projects/eclipse-cs/files/latest/download下载插件,links plugin的方式安装到eclipse中

重点看使用体验:针对一个项目启动checkStyle之后

 

刚检测完,项目所有类都泛黄,甚至有的类打上了小红叉。静心仔细的看下:

 提示有:public修饰符多余,一行超过80个字符,缺少文档注释,魔法数字等等等等。出现中文代码的地方会直接打红叉。非常精细。

蓝色区域:包括Errors和warnings。Errors重点列出了代码中的问题,比如代码中有中文等等,Warnings:一般是代码风格不好的地方。比如代码

最后一行加注释算是不好的风格

紫色区域:发现用于特定的注释分割,很多注释都有问题,和eclipse自动格式化有冲突,我觉得可以是忽略的。左边的小放大镜有详细的不规范信息。

    个人觉得检查的精度还是很细的,但是对于注解等的检测过于精细,很扰乱视眼。Errors级别就可以了。和eclipse的格式化有很多的冲突。导致很多也是不必要的检验。

    当然,也是可以自定义设置检测项,如下:

 

这样可以把注释的或者和日常格式化有问题的检查撤销掉。主要检查代码的问题。不过说实话这个设置起来还是很麻烦的,我觉得还不如将就着看所有的提示

信息。不需要改的就人为忽略吧。重点关注Errors。从中看出,这款其实主要侧重的还是代码的风格的检测。

3、PMD

  下载安装:http://sourceforge.net/projects/pmd/files/pmd-eclipse

  安装完成,针对一个项目启用PMD检测,进入pmd工作视图

  如上图:

    蓝色区域:是汇总的有问题的代码,pmd将代码分析完之后的级别划分的更细致了,就是5个小圈对应:error high, error, warning high, warning,infomation。

    绿色区域:定位,并且给出问题描述。

      这款代码检测软件应该说是和findbugs是属于一种类型的。在我检测的代码中,被pmd标记最多的是代码中有system.out.println() ,的确在正式项目中不应出现这种语句。

  同样方法的大小写是否符合规范也会检查出来。在代码检测中也更多的关注error high,error。

    另一方面pmd插件有20M大小,说明他的检测会很细致,也很广泛。

总结

对于工具的探索就到这,因为我觉得这三款工具对于现在项目的检测力度已经够用了。而jtest由于是商业非开源产品,就不去下载破解版验证了。

现在对以上工具进行一下总结:

findbugs:非常好用的一款代码检测工具,检测的深度比较深,对代码中渗透的性能,内存的使用释放有很好的检测。能检测出可能导致错误的代码,如空指针引用等等。

          我觉得这款检测工具应该是首选必备的。

checkstyle:顾名思义,他就是一款检测代码风格样式的工具,对ccs都会有检测,可以用以辅助提高开发过程中的代码风格。缺点很多检测过于细致和格式化冲突,

比如注释都会高亮显示,很扰乱视线,所以使用时候组号可以做自定义的风格规范。对代码的bug发现力度较弱。

PMD:20M大小,说明他的检测非常的广,在我看来的确也是,system.out.println 也会做为error提示。一些命名的检测等。的确并不是代码的bug,

而是项目中代码的规范检测。深度没有findbugs深,在使用findbugs的情况下,可以配合pmd来检测一些不规范的代码。

能通过以上这三款软件的检测,并加上业务代码的review。相信代码的质量级别应该是很高了,相信也能很好的满足日常的开发和后期的维护了。

[转载]非小型电子商务系统设计经验分享

mikel阅读(1100)

[转载]【非编辑推荐】非小型电子商务系统设计经验分享 Coding changes the world – 活雷锋 – 博客园.

前言

做了两年多针对淘宝的电子商务数据线下数据系统,越到后面越觉得自己还没入门,不管技术上还是业务上,这篇文章既是对自己的积累的一次梳理,更想的是能在和各位朋友交流中,互相进步。

ps:所有字段并不是正式项目所使用字段,请根据自己的业务需求进行酌情查看处理微笑,类目属性,商品,订单结构可以参考淘宝API数据接口进行查看具体字段。

商品模块设计

商品模块是支撑整个架构的核心,如果这块没设计好,那么所有后期的复杂的统计需求基本都满足不了。

商品关系

 

为什么这样子设计属性看这里这里,把品牌从类目中剥离出来是为了降低程序针对商品属性这块的复杂度。这里通过淘宝的添加宝贝的操作来说明上面的数据结构如何满足下面的需求:

 

image

 

 

image

 

 

image

PS:本来要截玉兰油沐浴露的图,结果发 现淘宝取消了以前选择毫升*买的多送得多组合SKU的添加商品方式,改成了一个SKU就是一个宝贝的编辑手段,呵呵,没办法,只有上面截个衣服的图,下面 的数据却是快消品的。淘宝这样做这也是没办法的,这种快消品不同SKU,图片还不能用一样的,而且大部分用户搜索的时候呢,会喜欢直接搜索具体的毫升数, 这也给我们提了个醒,不同的类目可能会是不一样的处理方式,就算是服装这种SKU相对标准的类目,也会有说在展示和搜索结果中,会放置一个产品的多个 SKU,比如凡客的网站,一件衣服的几个颜色都会出现在类目搜索结果中,增加曝光度,吸引用户点击购买。

 

页面属性的编程实现可以参考这里。SKU存放在产品SKU表中,按我们的实际需求增加修改字段,比如我的表中多了ProductCode和BarCode字段,SKU的属性会拆分后存入产品基本属性值表,便于搜索或统计等需求。商品的基本属性全部打横存入商品的基本属性表中,那么SKU表的存储如下:

image

 

那么这个item是4013的产品在基本属性值表中的数据存储如下

image

这里我是把所有的属性都打成一条一条存储在这个表中,那么能满足我们在日常业务的属性搜索,统计等需求。按属性搜索,这里必须要注意以下几点:

1.不可能所有的属性都开放给用户或者我们的客户进行搜索,所以我们会在属性名表中有个字段(是否搜索字段)来人工控制哪些属性是搜索属性

2.基本属性是同一个宝贝下面所有SKU都共有的,SKU属性是单个SKU独有的,所以搜索的时候还必须分清楚销售属性(销售属性组成SKU)和基本属性。

3.属性图片的存储我并没有设计,因为我们是做快消品,没有这个需求。但是,如果我做的话我还会是在基本属性值表中加上”是否图片属性,是否使用默认图片,图片URL“3个字段来记录颜色属性。做属性搜索的时候比较方便。

4.产品通过关键字搜索和属性搜索是分开 的,两种搜索并不是一种解决方式,比如淘宝,在首页的搜索框是通过分词匹配宝贝标题的关键字,通过关键字的匹配程度,店铺的dsr评分权重来决定搜索结 果,而属性搜索的时候则是匹配满足属性条件的宝贝。那属性又分第1点和第2点,所以还是挺麻烦的。

 

那到了这里产品的存储已经说完了,其它的运费什么的,就懒得说了。

 

这里你会发现有打包品表,打包品子表,最终商品表,商品变更记录表。这里需要详细说明一下。

 

首先说一下打包品概念:

打包品:为了各种运营上的需求,很多时候我们会人为的把多个SKU组合成一个商品进行组合销售,我们在淘宝购物的时候,经常会看到这样的情况,A产品+B产品组合销售,AB的组合在淘宝上面表现为一个宝贝,你看看这里或者这里或者这里,这些就是拉。这种销售数据在订单里表现是一个淘宝商品,但是我们要做库存管理,数据分析等需要拆分出来。这是必须考虑的!

PS:有那种出厂打包品,比如一个包装盒里面有香皂,有沐浴露,但是它们本身就是一个SKU,出厂就这样,所以不能和打包品混为一谈。

 

 

由于我们运营上的需要,我们可能卖单个SKU,也可能卖多个SKU的组合,那么在我这里把单个SKU和多个SKU组合都看成打包品,单个的SKU打包品它的子项只有它自己,这样做的好处是,系统中只需要一种方式来处理这种关系。在打包品表中通过类型来区分。

这里有一个关键问题要注意,我们在出售商 品过程中,价格是可能会随时人工或者系统来干预变化的,比如产品A标题叫B洗发水+C护发素直降20元,但是我们根据实际的流量和转换率价格可以上下浮 动,那么我们就要及时的调整价格,所以我们的标题,价格都需要进行更改,这里牵涉2个问题,我们是新建一个打包品或者我们是另外放在最终商品表,我们就需 要修改对应的标题和价格,同时呢,在商品变更记录表中记录添加一个上次修改的备份,作为我们对不同价格的转换率的一个分析基础数据。第二个问题就是由于修 改了打包品或者创建了新的打包品(SKU子项,SKU数量一样)价格,那么对应的分配到每个具体SKU的价格发生了变化,这里如果是新建了打包品就没问 题,但是如果是修改打包品,那么我们对打包品SKU子项的价格就必须通过相应的公式进行计算。比如A+B+C今天是100元,A是30,B是50,C是 20,如果价格变成了90或者110,那么对应到具体的子项价格也需要更改,因为很经常的需求就是统计某产品或者某SKU的销售量和销售额。

所以最终是我们网站上是出售打包品还是最终产品,是每次新建打包品还是修改,这要看自己权衡。但是商品的价格变更是一定要记录的。一是留备份,二是分析价格对销售的影响等等。

 

 

这样设计遇到的问题

1.起初产品维护人员意见很大,觉得很复 杂,工作量很大。因为这种结构需要维护人员维护属性,并且需要他们懂一些专业知识和熟悉整个流程,各种名词搞得他们头晕,后面甚至出现了相当大的负面抵触 情绪。这个没办法,因为我们这个不是网站,不是说让你简单的舒服的就能添加一个商品,这个需要掌握分类-产品-属性-打包品之间的业务关系以及这样维护的 好处。解决办法:1.慢慢沟通,说明增加的工作量是为了他们在出复杂的报表的时候不需要手动去进行筛选,而且基础数据维护好了,一劳永逸。2.一定要培训 好产品维护人员,让他们在有相关业务背景人员指导下能清晰的分清楚属性的意义,以及根据业务规则维护好属性基础表和录入产品等信息。

2.由于一开始数据的关联检查机制没做好,导致后面乱了很多数据,所以在后面又来花时间检验数据和建立起相应的检查机制,浪费了很多时间。

 

订单模块结构

image

 

这里关系很简单,我想着重说明3个问题,

1个就是订单主表中存储地址库ID+买家具体地址组合成购物地址,不是依赖用户收货地址的信息,因为用户的收货地址是可能发生人为的修改的。

2.地址库的城市级别,这是一个统计维度,最好加上

3.在订单子表中,不仅存储了打包品的ID,还会把当时网站上该商品的标题和SKU名字,以及当时的价格存储进来,这是很有必要,不能直接使用关联的打包品或者商品的价格,标题,前面说了,随时可能改变的。

4.促销信息表,这里就是记录所有促销活 动信息,一个商品可能对应多个促销活动,比如使用了优惠券+(满200-20)+ 满100包邮+VIP优惠10元+XXX。这样的设计是比较好的。从订单角度来看这个订单应用了多少个活动模型,能准确的抽取某种促销活动的所有订单。

不要把这种东西设置产品表中,或者与产品表进行关联,先不考虑其它原因,首先把业务模型和产品模型混在一起就乱了。

 

活动模块设计

image

由于我们的订单表有订单活动促销信息表与 其关联,那么实际上我们统计一般的需求只需要关联过来活动模型表就能得到说某个活动或者某类活动的数据情况,这里对于前台商品活动信息是个悲剧,一套活动 缓存跟新机制,前台所有商品显示的时候和所有订单提交时检查是否满足所在时间内的活动模型来展示不同的UI。

还会有很多活动模型,这里只是列了几个,另外,必须注意一个问题,只要涉及到包邮的地方就要考虑有的地方不能包邮。也可以单独存储不包邮的城市。这个就要看业务上如何决定这个模型怎么建立。

 

访问跟踪模块设计和CRM

访问数据这一块我们是结合量子统计和自己跟踪的数据进行合并数据出数,这块我就过掉了,因为这块感觉我们并没有做太深,今年会单独把这块加强。我只想说,这块很重要。这一块是电子商务运营过程中的重中之中,没有他,几乎所有的带有指导性的报表数据你都没办法出。没有他就没有转换率,没有它,你就不知道站外推广的效果如何,没有他,你就不知道网站栏目,活动标题,图片等怎么去改版,甚至商品怎么放置!!!等等等等….

CRM也是一样,我们现在的弱项,我们现在着重统计用户回购率,对品牌忠诚度等一些现在业务上比较关注的面,没有钻深,但是这样光这样说好像有点太那啥,我放点收集的资料吧,这也是我们下一步努力的目标。

建立CRM的最本质目就是获取、保持和增加可获利客户(消费者)。

Image

imageImage(1)

 

 

我认为好的数据报表展现

我认为好的二维数据统计分析报表必要的条件:

1.多种直观的,美观的图形报表展现 (图1)

2.对应的数据表格展现 (图1)

3.对应的名词解释 (图1)

4.相关业务报表的关联查看 (图2)

5.能窜起业务流程,(图3)

图1:

image

图2:

image

图3:

 

我自认为我们公司的报表的美观,直观,清晰度都做的不错了,但是看到另一家公司的报表之后(就是后面2张),我直接给他们跪下了。相关数据一目了然,通过数据窜起了整个站点流程。多好的产品经理啊~!建议大家做的时候可以参考这家,量子后台的也不错。

 

后记

现在越来越多关注运营,营销推广和各种商业模式,技术反倒变成相对不太重要的东西。路漫漫,何其修远兮,努力吧。

[转载]优秀工具:推荐16款免费的在线二维码生成器

mikel阅读(1774)

[转载]优秀工具:推荐16款免费的在线二维码生成器 – 梦想天空(山边小溪) – 博客园.

二维码(QR Code),又称二维条码或二维条形码,是用某种特定的几何图形按一定规律在平面分布的黑白相间的图形记录数据符号信息,二维码在代码编制上巧妙地利用构 成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息。今天这篇文章向大家推荐16款免费的二维码 生成器,帮助你在线生成二维码。

BeQRious

BeeTagg

QR Code Generator

Maestro

ZXing Project

QR Treasure Hunt Generator

Delivr

AZON Media

2CodeMe

QR Stuff

Kerem Erkan

I-nigma

QR Jumps

AT&T Create-a-Code

URL 2 Tag

[转载]利用FlexCell实现的一些报表统计应用

mikel阅读(990)

[转载]利用FlexCell实现的一些报表统计应用 – 伍华聪 – 博客园.

FlexCell表格控件有很多亮点的功能,使用也算比较方便,很多时候,可以模拟传统的Excel内容在Winform界面上展现,而且也支持内 容格式的预设置等,本文主要介绍利用这个控件来实现一些特殊的统计及关联信息显示的功能。在实际项目中,有一个这样的需求,首先需要显示一些数据的总的汇 总数据,每条又可以进一步查看其对应的明细数据,类似于数据仓库的钻取功能,那么我们要如何实现以上的功能呢?

1、报表统计功能介绍

功能介绍如下图所示。

单击链接可以进一步展现相关的人员列表,如下图所示。

2、功能实现思路分析

实现以上功能,如果利用.NET自带的DataGridView,通过在在Cell里面添加链接控件的方式,估计也能实现,不过可能操作起来会比较麻烦一些。

本文主要介绍利用FlexCell 表格控件实现以上操作的思路,共大家参考,并非为该控件做广告。

首先我们需要在为每个条件显示一行记录,对应还要记住它的条件,其中有一个超链接的单元格,供我们单击可以查看明细。为了记住该行的条件,我其实在一个隐藏列里面放置了条件表达式,真实情况下如下所示,只是为了显示美观,不需要显示隐藏的条件内容。

FlexCell提供超链接的单元格类型,设置为超链接类型的单元格,内容会加下划线,而且可以对超链接的事件进行捕捉处理。

得到了响应的处理事件,以及存放了对应的条件描述,那么在事件里,打开一个新窗体,根据条件内容获取对应的列表显示出来即可。

3、功能实现代码

1)绑定相关的汇总数据

Dictionary<string, CListItem> dict = GetAgeCondition();
 if (dict.Keys.Count > 0)
 {
     grid1.Cell(startRow++, 1).Text = "年龄段情况:";

     foreach (string key in dict.Keys)
     {
         CListItem item = dict[key];
         grid1.Cell(startRow, 2).Text = key;
         grid1.Cell(startRow, 3).Text = string.Format("有{0}人,占比例{1} ", item.Text, item.Value);
         grid1.Cell(startRow, 4).Text = string.Format("Age:{0}", key);

         startRow++;
     }
 }

2)超链接处理事件代码

在控件上单击Grid的Hyperlink处理事件,然后实现其内部处理代码,如下所示。

private void grid1_HyperLinkClick(object Sender, FlexCell.Grid.HyperLinkClickEventArgs e)
        {
            FlexCell.Cell cell = grid1.Cell(e.Row, 4);
            if (cell != null && !string.IsNullOrEmpty(cell.Text))
            {
                FrmStatisticDetail dlg = new FrmStatisticDetail();
                dlg.KeyCondition = cell.Text;
                dlg.ShowDialog();
            }
            e.URL = "";
            e.Changed = true;
        }

3)明细窗体处理代码

private void DealAge(string condition, ref int startRow)
        {
            grid1.Cell(1, 1).Text += string.Format("(条件:年龄{0})", condition);

            startRow++;
            // 设置单元格文字
            grid1.Cell(startRow, 1).Text = "年龄";
            grid1.Cell(startRow, 2).Text = "编号";
            grid1.Cell(startRow, 3).Text = "姓名";
            grid1.Cell(startRow, 4).Text = "性别";
            grid1.Cell(startRow, 5).Text = "生日";
            grid1.Cell(startRow, 6).Text = "职别";
            grid1.Cell(startRow, 7).Text = "部别";
            grid1.Range(startRow, 1, startRow, 7).BackColor = Color.Yellow;
            int start = startRow++;

            string where = "Age " + condition; 
            if(condition.Contains("~"))
            {
                string[] conArray = condition.Split('~');
                where = string.Format("Age >={0} and Age<={1}", conArray[0], conArray[1]);
            }

            DataTable dt = BLLFactory<Pilot>.Instance.FindByView("PilotAgeView", where, "Age", true);
            foreach (DataRow row in dt.Rows)
            {
                grid1.Cell(startRow, 1).Text = row["Age"].ToString();
                grid1.Cell(startRow, 2).Text = row["PilotNo"].ToString();
                grid1.Cell(startRow, 3).Text = row["Name"].ToString();
                grid1.Cell(startRow, 4).Text = row["Sex"].ToString();
                DateTime birthday;
                if (DateTime.TryParse(row["Birthday"].ToString(), out birthday))
                {
                    grid1.Cell(startRow, 5).Text = birthday.ToString("yyyy-MM-dd");
                }
                grid1.Cell(startRow, 6).Text = row["OfficialRank"].ToString();
                grid1.Cell(startRow, 7).Text = row["DeptCode"].ToString();

                startRow++;
            }
            FlexCell.Range range = grid1.Range(start, 1, startRow, 7);
            range.set_Borders(FlexCell.EdgeEnum.Outside | FlexCell.EdgeEnum.Inside, FlexCell.LineStyleEnum.Thin);

            startRow++;
            startRow++;
        }

上面的代码,主要就是动态绘制表头,设置格式,然后绘制表格明细的单元格内容即可。

4)实现打印、导出Excel/PDF等功能

控件内置了很多导出功能,实现基本的导出、打印功能,非常方便,代码如下。

private void menu_Preview_Click(object sender, EventArgs e)
        {
            try
            {
                grid1.PrintPreview();
            }
            catch (Exception err)
            {
                MessageDxUtil.ShowError(err.Message);
            }
        }

        private void menu_PageSetting_Click(object sender, EventArgs e)
        {
            try
            {
                grid1.ShowPageSetupDialog();
            }
            catch (Exception err)
            {
                MessageDxUtil.ShowError(err.Message);
            }
        }

        private void menu_ExportExcel_Click(object sender, EventArgs e)
        {
            try
            {
                #region 保存Excel文件
                SpecialDirectories sp = new SpecialDirectories();
                SaveFileDialog dialog = new SaveFileDialog();
                dialog.Filter = "Excel(*.xls)|*.xls|All File(*.*)|*.*";
                dialog.Title = "保存Excel";
                try
                {
                    dialog.InitialDirectory = sp.Desktop;
                }
                catch
                {
                    dialog.InitialDirectory = "C:\\";
                }
                dialog.RestoreDirectory = true;
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    string fileToSave = dialog.FileName;
                    if (string.IsNullOrEmpty(fileToSave))
                    {
                        return;
                    }

                    bool success = grid1.ExportToExcel(fileToSave, true, true);
                    if (success)
                    {
                        Process.Start(fileToSave);
                    }
                }
                #endregion
            }
            catch (Exception err)
            {
                MessageDxUtil.ShowError(err.Message);
            }
        }

        /// <summary>
        /// 导出Excel文件
        /// </summary>
        /// <param name="fileToSave">文件路径</param>
        /// <returns></returns>
        public bool ExportExcel(string fileToSave)
        {
            bool success = grid1.ExportToExcel(fileToSave, true, true);
            return success;
        }

        private void menu_ExportPDF_Click(object sender, EventArgs e)
        {
            try
            {
                grid1.PageSetup.DocumentName = "统计报表";
                bool success = grid1.ExportToPDF("");
            }
            catch (Exception err)
            {
                MessageDxUtil.ShowError(err.Message);
            }
        }

[转载]ASP.NET页面错误处理

mikel阅读(1186)

[转载]ASP.NET页面错误处理 – 八神吻你 – 博客园.

ASP.NET页面错误处理

 
ASP.NET应用可以再代码中利用异常捕获来处理错误(try、catch)但是应用出现的所有错误,都用异常捕获来处理是一种不良的编 程习惯。try、catch使用简单,但是过度使用会导致严重的性能损失。如果能检测可能出现不预期情况,应该尽量使用其他方法,而将异常捕获作为最后考 虑的手段,异常捕获是NET官方提供应用程序处理工具。如果ASP.NET应用程序出现异常,运行时会试图寻找有意捕获它的代码块,异常会在栈中下上遍 历,直到抵达当前应用程序的起始层,如果在这期间没有找到合适的处理程序,该异常便成为未处理异常,并使CLR抛出系统异常,用户会看见报黄页,应用程序 也会随即终止。
默认的错误页面用户在本地和远程计算机看到的典型错误页面是不一样的。
本地用户会看到相对详细的错误信息,远程用户接收到的信息不会那么详细。主要是出于安全方面考虑。
ASP.NET 提供了两级全局拦截点,分别位于页面级和应用程序级,帮助我们以编程的方法处理错误。基于基类Page暴露了Error事件,我们可以在页面中重写它,捕 获页面执行期间引发的未处理异常。同样,HttpApplication类中也有Error事件,用于捕获整个应用程序中抛出的异常。

 
页面级错误处理

 

protected override void OnError(EventArgs e)
{
Exception ex = Server.GetLastError();
if(ex is NotImplementedException)
Server.Transfer(“errorpages/notImplemented.aspx”);
else
Server.Transfer(“errorpages/apperror.aspx”);
Server.ClearError();
}

 
通过Server对象的GetLastError方法,我们获得被引发的异常。然后传到特定的页面,展示给用户。最后一旦异常处理完成,应用调用ClearError清楚错误。

 
全局错误处理

 

页面的Error事件处理能够捕获特定页面的错误,如果要使组合应用程序的所有页面共享一套错误处理代码,我们可以在应用程序级创建一段全局错误处 理程序,便可捕获所有未处理异常。实现几乎和页面级的错误处理程序一样,只不过处理的是代表整个应用程序的HttpApplication对象中的 Error事件,为此,我们需要将global.asax文件添加到程序中,并定义Application_Error方法:

 

protected override void OnError(EventArgs e)
{
Exception ex = Server.GetLastError();
if(ex is NotImplementedException)
Server.Transfer(“errorpages/notImplemented.aspx”);
else
Server.Transfer(“errorpages/apperror.aspx”);
Server.ClearError();
}

 
错误的页面映射

 
当未处理异常抵达调用栈末端时,ASP.NET会报黄页,但是我们还可以通过使用应用程序Web.Config文件中的<customErrors>区段,可以对这个功能进行充分的控制。

 

<configuration>
<system.web>
<customErrors mode=”Off”>
</customErrors>
</system.web>
</configuration>

 
mode属性用于决定错误消息是启动、关闭还是只显示给远程客户端。
当mode设为RemoteOnly时,远程用户会收到一般性的错误提示页面,而本地可以得到详细的错误信息。
当mode设为off时,本地和远程用户都会显示包含详细错误的页面。
可以自定义错误页面

 

<configuration>
<system.web>
<customErrors mode=”Off” defaultRedirect=”GenericErrorPage.htm”>
</customErrors>
</system.web>
</configuration>

 
如果mode为on时,本地和远程都将会把defaultRedirect=”GenericErrorPage.htm”页面来代替原来的错误报黄页面,为用户展示友好的错误提示,大多数自定义的错误页面时纯HTML编写的。

 
常见的HTTP错误处理

 
ASP.NET还使我们能够针对发生的不同HTTP错误显示相应的自定义页面。错误页面和特定HTTP状态之间的映射,可以定义在<customErrors>区段,设置<error>标签,将HTTP状态码

和自定义错误页面关联起来:
<configuration>
<system.web>
<customErrors mode=”Off” defaultRedirect=”GenericErrorPage.htm”>
<error statusCode=”403″ redirect=”NoAccess.htm”/>
<error statusCode=”404″ redirect=”FileNotFound.htm”/>
</customErrors>
</system.web>
</configuration>

[转载]Sql效能优化总结(续)- 架构调整篇

mikel阅读(941)

[转载]Sql效能优化总结(续)- 架构调整篇 – Jerry Tian – 博客园.

SQL效能优化总结(续)架构调整篇

 

看到几个博友对笔者的这个话题挺感兴趣,今天终于可以静下心来继续总结,废话少说,入正题。

 

SQL Server2005到SQL Server 2008和现在的SQL Server2012,微软好像一直没有推出负载均衡组件,如类似Oracle RAC的技术,不能实现负载均衡。由于数据库的高并发及横向扩展是经常遇到的问题,不少用户遇到这样的情况时考虑的方案是移植到Oracle平台,采用 RAC来解决问题。这是个很艰难的过程,成本相信也将是巨大的。也考虑过一些第3方方案。如:Moebius等,由于具多限制,也未能采用。所以只能利用 MS现有技术了,介绍前先简单提下MS的SQL平台的几种技术:

 

故障转移集群

Microsoft Cluster Server(MSCS)是一个提升可用性的技术,当一台服务器出现故障是可以段时间切换到其他服务器继续提供应用服务,不能实现负载均衡。

SQL Server 2005镜像和快照
该技术利用镜像来提高可用性,利用快照服务提供数据访问服务。

镜像是大家都熟悉的一个概念,快照是SQL Server 2005中引入的新特性。快照是某一个时间点上的数据库的克隆。快照的生成一般只需要几秒钟,创建快照时其实没有复制任何数据。对镜像数据库创建了快照 DB,快照DB就可以给用户提供数据服务器。但是由于是快照,实时性较差,所以使用时要考虑是否适合一些实时应用。如果要想把负载分散在主数据服务器和镜 像服务器上,就要阶段性的创建快照。

复制、订阅
SQL Server提供了复制技术(Replication),可以使现读写分离。有几种方式,基于快照、基于log和复合方式等,由于在本例中没有使用,在此不再赘述。

 

数据服务器架构调整:

由于公司几年前的应用逐渐增多,已经出现过一次系统效能问题,当时对数据服务器的硬件已经升级过一次了,但是由于近几年系统的不断增加,又一次达到 临界点,但是目前的数据服务器在一般的企业应用中基本已经算是顶级配置了,再进行硬件升级的话,一定需要一笔不小的开支。经过分析,其实现有系统中一部分 功能是不需要进行对数据的更新的,只需要读取数据(一些报表、查询等),于是决定把现有的服务器架构进行调整,进行读写分离,把一些唯读即可满足的功能, 转移到其他服务器,以此来分担现有的主数据服务器的压力。

 

原来的数据服务器架构,就是一般的客户端直解连接连接数据服务器,如下图:

 

调整后的数据服务器架构:

 

其实目前这个架构还不是最佳方案,采用复制技术应该在当前Case应该是最合适的,一是对系统的修改量最小,只要改变数据连接就可以达到目前的效 果。二是复制技术相对目前采用的镜像+快照方案技术上也是相对成熟的,但是之前由于出现过一次故障,所以该方案没被采纳,直到现在都还在愤愤不平, (TMD公司政治,不说了),数据服务器架构调成后,客户端的一些唯读功能,可以直接连接到镜像服务器,但是镜像是一种数据容错方案,若你提供镜像服务器 上更新数据的话,可能会发生意想不到的情况(具体没有测试,不妄加评论),而我们又想提供数据服务给客户端,该怎么办呢?SQLServer2005提供 了快照功能,建立快照DB后就象访问原DB一样,可以增删改,快照DB刚开始创建时其实是没有数据的,只是记录变化的记录信息,具体快照DB的原理,大家 可另外了解,在此不再赘述。

这种方案有个缺点,创建了快照DB后,用户访问快照DB相当于访问创建快照DB的那个时间点的原有DB。

例如:原DataBase名字为DB1,在上午10:00整时创建了快照DataBase,名字为DB2,用户访问DB2就相当于访问10:00点整时的DB1,快照嘛,顾名思义。

 

由于以下2个原因,就需要我们不断的删除旧的快照DB,创建新的快照DB提供数据访问服务。

 

  1. 客户端的用户永远想访问最实时的数据
  2. 快照DB的数量不能太多,因为增加快照DB会增加服务器负荷(因为他要同步源DB和每一个快照DB的差异数据)

 

由快照DB提供给用户访问的数据同原来的服务器中的数据有一个时间差,我们定为10分钟,这个时间差对一般的应用用户还是可以接受的。

 

经过迁移一些耗费服务器资源(主要是逻辑读,这个是服务器操作的瓶颈)较大的报表或查询到镜像服务器,有效减少了主数据服务器的压力,事实证明效果 还是比较明显的。经过以上努力,合理分担了主数据服务器的压力到其它不是很重要的服务器上,从某种意义上来说达到了负载均衡的效果。到此,关于数据服务器 架构调整先告一段落,后续再介绍具体的sql优化。

 

未完待续。