|
2008年10月25日
#
到阿里工作快一年了,这一年回顾来最主要的成果还是在这里成功了推行了自己的领域建模方法与对应的技术框架。总是想同更多的同学分享一下。 为了帮助更多人了解如何应用领域建模知识在实际项目中,我在InfoQ中发表了一篇题为“ 大型网站复杂业务持续重构之道——全程领域建模实践”的文章。为了增加趣味性,我用了个故事来展开内容,请大家不要对号入座(特别是老板们)。请有兴趣了解这一话题的同学可以阅读一下 原文在 http://www.infoq.com/cn/articles/sb-complex-business-continuity-refact
我一直期望可以提供一个高性能、高定制性的电子商务套件,它不应局限于传统的ERP应用或简单的线上商城应用。而是应将线上发生的产品展示、线上营销、在线客户服务与线下的仓储发货、经营分析、关键客户识别有机结合起来。
开始Opentaps开发已经两年多了,对它的理解也越来越深。做为一个完整的ERP套件来说我觉得它足够应付我们的日常业务:客户、订单、仓储、采购、财务等。并且它从应用层和开发层都已经提供了足够的支持与扩展空间。
我始终觉得Ofbiz提供的ecommerce模块过于简单,或者说它与我们国人想象中的电子商城有比较大的差距。如果我们在这之上来开发的话可以需要付出很多的effort才可能达到我们想要的效果。当然opentaps也提供了另一个选择gracious,但从个人角度来说我并不喜欢这个模块,它为用户提供的定制选项太少了,只能作为有经验的开发者定制自己应用的一个基础。
在为Opentaps开发Magento开发Integration Feature时,我接触到了Magento这个最广泛被使用的电子商务套件。不过当时只是浅尝即止并未做太多的功能研究,最近这些日子里我在不断深入了解了这个产品后越来越叹服其功能的强大。我想我想要的所有功能应该都被包括在这个产品已完成的feature中了。但同样我更期望是一个完整的线上线下相贯通的网络商城运营方案,离这样的要求Magento在财务结算、仓储管理、采购管理这些ERP功能方面又明显的偏弱。
我想Opentaps与Magento的整合可能不应仅限与原来所提供的产品、订单信息的贯通,或许我们应该尝试提供更密切的信息联系。至少第一步应该要把客户资料信息进行双向的同步,然后是库存信息。
考虑更远一点的话,在Magento或Opentaps中引入在线客服与呼叫中心的Feature也是一个非常让人振奋的事情。发展到这一步,我们就可以简单的集成这两个优秀开源产品来为我们的线上线下提供全方面的服务了。
在以后的文章里,我会继续在这个方面做更多的设计与实现工作。如果有童鞋对此话题感兴趣,可以把你的想法告诉我看是否可以一起来做点事。
BTW: 现在opentaps已完成的magento整合内容可以在这里找到 http://www.opentaps.org/docs/index.php/Opentaps_Magento_Integration
现在google,yahoo这些免费的群组都在大陆不好用,打算自己装个群组系统给自己的team用。用万能的google出Simple Groupware,看了特性描述好象还不错。那就先装上它试试吧。
1、安装apache、mysql、php
yum install mysql mysql-devel mysql-server php php-cli httpd
2、安装php所需的支持的包
yum install php-zlib php-gd php-mysql php-mbstring
3、启动mysqld与httpd服务
4、创建/var/www/html/sgs目录,并在该目录下下载Simple Groupware installer文件后解压,这样在/var/www/html/sgs目录下存在sgs_installer.php文件
5、打开浏览器访问http://your_server/sgs/sgs_installer.php选择你要的版本上面的Install,然后按照屏幕提示作下去就好啦
打算在自己申请的Free Amazon EC2上放个Magento,看了下Magento的帮助也整理内容如下:
1、安装apache、mysql、php
yum install mysql mysql-devel mysql-server php php-cli httpd
2、安装php所需的支持的包
yum install php-dom php-mcrypt php-gd
3、创建magento数据库
4、下载magento full release package并解压到/var/www/html下,并设置目录权限为777
5、通过浏览器访问安装magento http://localhost/magento
现在在用一个20寸的厦新电视机在做我的显示器,它是宽屏的,最高可以支持1344x768的分辨率。可是装了Fedora 14以后,只能发现最高支持的分辨率是1024x768。字体看起来都是扁扁的,很不爽!
得想个法把这个分辨率调整过来:
1) 打开一个Terminal窗口,切换到root身份后运行
Xorg -configure :1
这个命令会在/root下生成一个叫xorg.conf.new的文件,我们运行以下命令把它移到/etc/X11下
mv /root/xorg.conf.new /etc/X11/xorg.conf
2) reboot系统
3)在终端中输入 gtf 1344 768 60 -x 命令, 这样就会在终端中显示一些内容. 类似下面的:
# 1344x768 @ 60.00 Hz (GTF) hsync: 47.70 kHz; pclk: 83.95 MHz
Modeline "1344x768_60.00" 83.95 1344 1408 1552 1760 768 769 772 795 -HSync +Vsync
把它复制到 /etc/X11/xorg.conf中的[Moniter]段中,复制后的文件中[Moniter]段如下:
Section "Monitor"
Identifier "Monitor0"
VendorName "Monitor Vendor"
ModelName "Monitor Model"
# 1344x768 @ 60.00 Hz (GTF) hsync: 47.70 kHz; pclk: 83.95 MHz
Modeline "1344x768_60.00" 83.95 1344 1408 1552 1760 768 769 772 795 -HSync +Vsync
EndSection
4) reboot系统后就会在System->Preferences->Moniters中发现1344x768的选项啦!
5) 收工!
在Opentaps 1.4版本我将AOP由原来的AspectJ替换为现在Aspectwerkz。结果我自己就首先在64bit的windows系统下遇到ant编译出错,现在把解决方法列在这里
将opentaps/opentaps-common/build-aspects.xml中的weave任务内容替换为以下内容(其实就是把原来的ant的替换jar内容任务分成两部来执行):
<!-- ================================================================== -->
<!-- Weave advice into target jars -->
<!-- ================================================================== -->
<target name="weave" depends="detect-dependencies" if="processRequired">
<echo message="[build] =========== Start Building Aspect (Weave) ============="/>
<java classname="org.codehaus.aspectwerkz.compiler.AspectWerkzC" fork="true">
<jvmarg value="-Daspectwerkz.definition.file=${aopConfig}"/>
<jvmarg value="-Daspectwerkz.transform.filter=no"/>
<!-- <jvmarg value="-Daspectwerkz.transform.verbose=true"/> -->
<!-- <jvmarg value="-Daspectwerkz.transform.details=true"/> -->
<arg value="-verify"/>
<classpath refid="local.class.path" />
<classpath>
<pathelement path="${build.dir}/classes/common"/>
</classpath>
<!-- below is the jars to post-process -->
<arg value="${ofbiz.dir}/framework/entity/build/lib/ofbiz-entity.jar"/>
</java>
<java classname="org.codehaus.aspectwerkz.compiler.AspectWerkzC" fork="true">
<jvmarg value="-Daspectwerkz.definition.file=${aopConfig}"/>
<jvmarg value="-Daspectwerkz.transform.filter=no"/>
<!-- <jvmarg value="-Daspectwerkz.transform.verbose=true"/> -->
<!-- <jvmarg value="-Daspectwerkz.transform.details=true"/> -->
<arg value="-verify"/>
<classpath refid="local.class.path" />
<classpath>
<pathelement path="${build.dir}/classes/common"/>
</classpath>
<!-- below is the jars to post-process, if these jar not effect each other you can put them in same pos, else please seperate them into another task -->
<arg value="${ofbiz.dir}/framework/common/build/lib/ofbiz-common.jar"/>
</java>
<java classname="org.codehaus.aspectwerkz.compiler.AspectWerkzC" fork="true">
<jvmarg value="-Daspectwerkz.definition.file=${aopConfig}"/>
<jvmarg value="-Daspectwerkz.transform.filter=no"/>
<!-- <jvmarg value="-Daspectwerkz.transform.verbose=true"/> -->
<!-- <jvmarg value="-Daspectwerkz.transform.details=true"/> -->
<arg value="-verify"/>
<classpath refid="local.class.path" />
<classpath>
<pathelement path="${build.dir}/classes/common"/>
</classpath>
<!-- below is the jars to post-process -->
<arg value="${ofbiz.dir}/applications/order/build/lib/ofbiz-order.jar"/>
</java>
<jar jarfile="${lib.dir}/${name}.jar" update="true" basedir="${build.dir}/classes/common" includes="org/opentaps/aspect/secas/**" />
<!-- put aop.xml into target jar META-INF dir -->
<mkdir dir="${build.dir}/classes/META-INF" />
<copy file="${aopConfig}" tofile="${build.dir}/classes/META-INF/aop.xml" overwrite="true"/>
<!-- create new jar with aop.xml, then copy it to orign location, using these for avoid unable rename error on windows 64bit system-->
<zip destfile="${lib.dir}/ofbiz-entity.jar">
<zipfileset src="${ofbiz.dir}/framework/entity/build/lib/ofbiz-entity.jar"/>
<fileset dir="${build.dir}/classes/">
<include name="META-INF/aop.xml"/>
</fileset>
</zip>
<delete file="${ofbiz.dir}/framework/entity/build/lib/ofbiz-entity.jar" failonerror="false"/>
<move file="${lib.dir}/ofbiz-entity.jar" tofile="${ofbiz.dir}/framework/entity/build/lib/ofbiz-entity.jar"/>
<zip destfile="${lib.dir}/ofbiz-common.jar">
<zipfileset src="${ofbiz.dir}/framework/common/build/lib/ofbiz-common.jar"/>
<fileset dir="${build.dir}/classes/">
<include name="META-INF/aop.xml"/>
</fileset>
</zip>
<delete file="${ofbiz.dir}/framework/common/build/lib/ofbiz-common.jar" failonerror="false"/>
<move file="${lib.dir}/ofbiz-common.jar" tofile="${ofbiz.dir}/framework/common/build/lib/ofbiz-common.jar"/>
<zip destfile="${lib.dir}/ofbiz-order.jar">
<zipfileset src="${ofbiz.dir}/applications/order/build/lib/ofbiz-order.jar"/>
<fileset dir="${build.dir}/classes/">
<include name="META-INF/aop.xml"/>
</fileset>
</zip>
<delete file="${ofbiz.dir}/applications/order/build/lib/ofbiz-order.jar" failonerror="false"/>
<move file="${lib.dir}/ofbiz-order.jar" tofile="${ofbiz.dir}/applications/order/build/lib/ofbiz-order.jar"/>
<echo message="[build] =========== Done Building Aspect (Weave) =============="/>
</target>
以上方法已经在opentaps wiki [ http://www.opentaps.org/docs/index.php/General_Installation_of_Opentaps#Build_Aspect_class_Errors] 中加上过了。
OFBIZ为我们提供了POS模块,它已经可以满足我们日常收银的大部分要求了。但是美中不足的是它没有提供库存查询功能。我也没看出来OFBIZ有近期实现此功能的计划,所以打算自己做一个。
首先研究一下POS模块的代码结构:
1、POS模块同其它模块的实现方式有所区别,它用了XUL技术来定制展示的界面。所有屏幕文件使用XUL格式定义于screens目录之下
2、按钮事件与java代码映射关系,记录在config\buttonevents.xml下
3、资源文件存放在XuiLabels_[locale].properties与config\PosUiLabels.xml中
了解结构后,加上库存查询功能就很简单了,我是这样做的:
1、在screens/default/menu/mgrmain.xml中增加一个mgrInquiryInventory库存查询按钮,它会出现在POS的“经理”界面中的
2、在buttonevents.xml中加上以下行,映射事件代码
<event button-name="mgrInquiryInventory" class-name="org.ofbiz.pos.event.ManagerEvents" method-name="inquiryInventory" disable-lock="false"/>
3、在ManagerEvents.java中加入inquiryInventory方法代码(我是使用了一个ShowInventory的XPage来展现当前选中项的库存分布)
4、完工。
算了算,包括阅读代码和编写这些东东总共也就花了七八个小时的effort,不知道为什么ofbiz就不肯加上这个功能呢?或者pos模块不再发展了?
原来opentaps中的entity model文件中大量使用了floating-point来定义数值内容,现在我们要使用fixed-point来替换它们,以保证它的java type使用BigDecimal而不是Double.
这样就有了这次的替换任务,把所有model文件中的floating-point替换成为fixed-point。
方法对我来说有三:
1, 在eclipse或其它ide中查找出所有的包括floating-point文件来手工修改
2, 在UltraEdit或其它文本编程器中进行批量文件内容替换
3, 尝试一下linux的下的sed命令?
评估一下,3肯定是一个最有效率的选择。那么说干就干,let's go!
命令如下:
sed -i "s/floating-point/fixed-point/g" `find . -name "*.xml"|xargs grep floating-point -rl`
一句话就搞掂了,真棒!
莫名其妙,日志中老是有无法连接oracle的异常
java.sql.SQLException: Io 异常: The Network Adapter could not establish the connection
先用oracle用户登录到服务器上
sqlplus /nolog
conn / as sysdba
运行了一些sql,一切正常呀,奇怪!
仔细想想,可能是lsnrctl的问题,打开listener.ora在下面插入以下内容:
(SID_DESC=
(GLOBAL_DBNAME = ORCL)
(ORACLE_HOME = /oracle/10g)
(SID_NAME = ORCL)
)
完成后的listener.ora如下
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = /oracle/10g)
(PROGRAM = extproc)
)
(SID_DESC=
(GLOBAL_DBNAME = ORCL)
(ORACLE_HOME = /oracle/10g)
(SID_NAME = ORCL)
)
)
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1))
(ADDRESS = (PROTOCOL = TCP)(HOST = localhost.localdomain)(PORT = 1521))
)
)
保存后,重启lsnrctl服务后就可以正常联接了。
因为/ 下的磁盘空间快用完了,所以将mysql的data目录迁移到/mnt/mysql下,先修改 /etc/my.conf:
[mysqld]
datadir=/mnt/mysql/data
socket=/mnt/mysql/mysql.sock
然后重启mysql, service mysqld restart
但始终提示启动失败,查看日志/var/log/mysqld.log,发现实际mysql已经启动。
编辑/usr/lib/mysql/mysql_config将
socket='/var/lib/mysql/mysql.sock'
修改为
socket='/mnt/mysql/mysql.sock'
问题消除。
但使用mysql本机访问时总是提示无法通过/var/lib/mysql/mysql.sock连接数据库,噢,原来是忘记在/etc/my.cnf中加入client配置了,
增加:
[client]
socket=/mnt/mysql/mysql.sock
后故障消除。怕自己以后迁移数据库忘记了,还是写篇blog记下为妙。
好象有日子没更新过blog了,写篇来证明博主的存在吧。
好多哥们建了网站,都不知道如何吸引别人的眼球。其实我觉得的,要么功能,要么内容。可是个人办的网站,就是二十四小时挂在网上不停的拷贝粘贴又能做得了多少内容? 懒人自有懒办法,写个程序去抓去?
嗯,想到就得动手,不然过阵就忘记了,当然喽,还是谋定而后动,打算怎么整呢?先选择好技术:
1、谁去抓?好象好多人都推荐使用nutch或是herixtrix,不过我觉得象一般的小网站玩玩,就不必这么费事了,就用HTMLParser吧,抓取与解析一体化解决吧。
2、谁来格式化数据?同上了,俺用正则习惯了,正好node + regex 够你用了。
3、如何存储抓来的数据? 我是用惯JavaEE了,那就 ibatis + mysql 吧,hiberate 就不必了,虽然我没打算做个多高负载的应用,但hibernate怎么也感觉不是很合适吧? (喜欢hiberate别拍砖,我不喜欢口水战,各人有自喜好,而且hibernate我也很喜欢)
4、如何把你抓来的数据表现出来呢? 这个就得看你自己的需要喽,CMS 还是 B2B电子商务程序? 还是一个 CRM或ERP程序(如果这样的话,倒不妨试试opentaps?)
5、性能是不是问题? 如果你打算做个专业的搜索引擎或是阿里巴巴之类的电子商务引擎,除去硬件的考虑、数据库的优化,也许你要更多地考虑cache的管理,还有可能考虑文件索引(如用lunece)
6、差点忘记了,谁来调度这些爬虫及程序之间的任务执行顺序呢? 试试Quartz?!
又犯困了,先写这吧,正好有个朋友要我帮忙指导做个类似玩意儿,下次再记录吧。
所有 HTTP 状态代码及其定义。
代码 指示
2xx 成功
200 正常;请求已完成。
201 正常;紧接 POST 命令。
202 正常;已接受用于处理,但处理尚未完成。
203 正常;部分信息 — 返回的信息只是一部分。
204 正常;无响应 — 已接收请求,但不存在要回送的信息。
3xx 重定向
301 已移动 — 请求的数据具有新的位置且更改是永久的。
302 已找到 — 请求的数据临时具有不同 URI。
303 请参阅其它 — 可在另一 URI 下找到对请求的响应,且应使用 GET 方法检索此响应。
304 未修改 — 未按预期修改文档。
305 使用代理 — 必须通过位置字段中提供的代理来访问请求的资源。
306 未使用 — 不再使用;保留此代码以便将来使用。
4xx 客户机中出现的错误
400 错误请求 — 请求中有语法问题,或不能满足请求。
401 未授权 — 未授权客户机访问数据。
402 需要付款 — 表示计费系统已有效。
403 禁止 — 即使有授权也不需要访问。
404 找不到 — 服务器找不到给定的资源;文档不存在。
407 代理认证请求 — 客户机首先必须使用代理认证自身。
415 介质类型不受支持 — 服务器拒绝服务请求,因为不支持请求实体的格式。
5xx 服务器中出现的错误
500 内部错误 — 因为意外情况,服务器不能完成请求。
501 未执行 — 服务器不支持请求的工具。
502 错误网关 — 服务器接收到来自上游服务器的无效响应。
503 无法获得服务 — 由于临时过载或维护,服务器无法处理请求。
-----------------------------------------------------------------------------------------------------------------------
HTTP 400 - 请求无效
HTTP 401.1 - 未授权:登录失败
HTTP 401.2 - 未授权:服务器配置问题导致登录失败
HTTP 401.3 - ACL 禁止访问资源
HTTP 401.4 - 未授权:授权被筛选器拒绝
HTTP 401.5 - 未授权:ISAPI 或 CGI 授权失败
HTTP 403 - 禁止访问
HTTP 403 - 对 Internet 服务管理器 (HTML) 的访问仅限于 Localhost
HTTP 403.1 禁止访问:禁止可执行访问
HTTP 403.2 - 禁止访问:禁止读访问
HTTP 403.3 - 禁止访问:禁止写访问
HTTP 403.4 - 禁止访问:要求 SSL
HTTP 403.5 - 禁止访问:要求 SSL 128
HTTP 403.6 - 禁止访问:IP 地址被拒绝
HTTP 403.7 - 禁止访问:要求客户证书
HTTP 403.8 - 禁止访问:禁止站点访问
HTTP 403.9 - 禁止访问:连接的用户过多
HTTP 403.10 - 禁止访问:配置无效
HTTP 403.11 - 禁止访问:密码更改
HTTP 403.12 - 禁止访问:映射器拒绝访问
HTTP 403.13 - 禁止访问:客户证书已被吊销
HTTP 403.15 - 禁止访问:客户访问许可过多
HTTP 403.16 - 禁止访问:客户证书不可信或者无效
HTTP 403.17 - 禁止访问:客户证书已经到期或者尚未生效
HTTP 404.1 - 无法找到 Web 站点
HTTP 404 - 无法找到文件
HTTP 405 - 资源被禁止
HTTP 406 - 无法接受
HTTP 407 - 要求代理身份验证
HTTP 410 - 永远不可用
HTTP 412 - 先决条件失败
HTTP 414 - 请求 - URI 太长
HTTP 500 - 内部服务器错误
HTTP 500.100 - 内部服务器错误 - ASP 错误
HTTP 500-11 服务器关闭
HTTP 500-12 应用程序重新启动
HTTP 500-13 - 服务器太忙
HTTP 500-14 - 应用程序无效
HTTP 500-15 - 不允许请求 global.asa
Error 501 - 未实现
HTTP 502 - 网关错误
为了进一步了解ofbiz/opentaps的ER模型,硬着头皮看了《The Data Model Resource Book》差不多两周。虽然还是有些地方有点吃不准,总体来说还是收益良多。我尝试将它的思想与我以往用过或是我了解的对业务建模的ER模型进行对比,可以在以后的开发中汲取经验:
1、实体之间的关系或是本身的描述都是有生命周期的,这一点从ER Diagram中随处可见的From Date 与Thru Date可以充分体现出来。
2、以第三方角度对业务建模而非“我”方角度建模,这个比较有趣,通常来说我们会把销售订单与采购订单分成两个实体,这是因为我们从使用者角度来看销售订单与采购订单是不同的业务类型,而从第三方角度来着,销售订单与采购订单都是当事人之间发生的采购/销售业务,关心的业务内容与属性完全一致,而这样建模可以带来大量的业务逻辑共用与可扩展性。
3、抽象层次较高的业务实体,通过属性内容进一步细化业务对象。
这是我感觉比较重点的几个点,先记下!
看opentaps关于库存管理的文档时,总是被Serialized Inventory Item与Non Serialized Inventory Item弄得一头雾水.
看了一下<<The Data Model Resource Book>>的这段话才弄明白:
The INVENTORY ITEM may be either a SERIALIZED INVENTORY ITEM,which means each item's serial num(ber) is tracked, or a NON-SERIALIZED
INVENTORY ITEM, which means a group of items is tracked together and the quantity on hand for these is maintained by their location.
即产品是按个体单独跟踪,还是按相同群体特征进行数据和存放地点跟踪管理.
记下来,不要下次再忘记了!
今天又是一阵头痛,上午网络超不好,老是连不入服务器,搞得服务器越来越慢,也不知道原因,在服务器上运行
netstat -anp|grep 服务端口号 |grep TIME_WAIT|wc -l 一看,几百个死连接在等待处理呢.
怎么办?上网查了下资料,原来是可恶的TCP/IP栈,调整一下timeout相关参数:
echo "30">/proc/sys/net/ipv4/tcp_fin_timeout
echo "1800">/proc/sys/net/ipv4/tcp_keepalive_time
echo "0">/proc/sys/net/ipv4/tcp_window_scaling
echo "0">/proc/sys/net/ipv4/tcp_sack
echo "0">/proc/sys/net/ipv4/tcp_timestamps
嘿,立竿见影,死连接数立即就下去了,很少看得到TIME_WAIT的连接了.
在同si chen的沟通中,他觉得pentaho是一个很不错的东东,如果把它同业务数据结合起来进行分析,可能会产生一些非常美妙的结果.于是有想好好了解一下pentaho的愿望.
本人的学习习惯一直的方针是:如果一个东东不能让我在30分钟内跑出它的demo,我暂时是不会去了解它的(窃以为这叫不够友好也不够成品化)
pentaho这点很争气,下载下来解压,双击"start-pentaho.bat" 文件就顺利的跑起来了. (不过究竟应该下载哪个文件让我头痛好久, 网上大多都是介绍1.x的安装,我睁大眼也没找到啥pre-install版本,犯迷糊的兄弟直接在 http://sourceforge.net/projects/pentaho/ 下载 Business Intelligence Server 吧)
接下来先看看它包含的内容 (http://www.pentaho.com/about/) :
依惯例开篇先吹水:
Pentaho是一个可以替代商业智能的开源方案.Pentaho Open BI套件提供全面的报表,OLAP分析,仪表板,数据集成,数据采集及商业智能平台,是世界领先而且广泛使用的开源商业智能套件.Pentaho商业开源业务模型减少了软件授权费用,提供支持,服务以及产品增强年度订阅.从Pentaho开创至今一直是开源商业智能的先锋,Pentaho产品已经被下载超过3百万次,产品应用于从小企业到全球2000强的企业中.
经验丰富的团队
由包括 Business Objects, Cognos, Hyperion, IBM, JBoss, Oracle, Red Hat, 及SAS协会等业界领导者供应商的退休人士创建
广泛的能力
报表 数据集成
分析 数据采集
仪表板 商业智能平台
高伸缩性
Pentaho是世界广泛部署的开源商业智能套件.可以提供给不同规模不同行业的企业使用.
Pentaho是在开源领域内首个且唯一的全面商业智能平台,全部来源于开源社区中.
Pentaho是唯一在开源商业智能套件领域内即是核心项目领导者也是赞助者的产品.其它整合非他们赞助或控制的第三方商业智能模块的商业智能套件,不能提供专业水准、服务承诺、或产品路线图。而Pentaho可以做到。
Pentaho商业开源业务模型减少软件授权费用,提供支持,服务以及通过年度订阅获得产品增强内容。
可伸缩的部署
完整,集成的商业智能平台
独立的,开箱即用的产品
为Java开发人员提供可嵌入的组件
高品质的专业服务
特制的支持提供
有经验的顾问及世界范围内的顾问合作者
一站式,公共,在线培训及专业认证项
提供使用Pentaho技术进行OEM服务
嗯,看着好象很不错的样子,以后每天看半小时!
本人译自 http://www.pentaho.com/about/ , 转载请注明出处!
摘要: 我们在制作单证或报表时,客户经常要我们把最后的合计数转写中文大写金额。这个需求很合理,但感觉并不容易实现,如何在JasperReport中加入大写金额的实现呢?提供一种实现的方法给大家参考。
实现思路:
在报表执行过程中使用scirptlet将存放着数字金额的变量读出转换成大写金额字符串后放入大写金额变量中。报表即可象显示普通字符变量一样显示大写金额。
TransChineseMon... 阅读全文
放一些屏幕截图来看图说话吧
流程设计界面
左边下拉框放至了我所有允许定义流程的业务对象类型,可通过底部的"新增"\"修改"\"删除"等操作进行维护.点击已定义的流程可以查看对应的流程设置
新增流程界面
新增时可为该流程设置相关启用条件,优先级别及审批步骤等
审批人的待办工作台,可以这里统一处理各类待办业务:
待办业务的查看界面:
审批流转界面(以通过为例)
下面给出UML图供大家参考:
其中:
流程类型、流程、活动、流转条件、外部应用为设计时对象,用于描述流程规则
流程实例、活动实例、活动记录为运行时对象,用于记录实际发生的流程运行状况
需要解决的问题是,如何将我们的应用与此工作流引擎进行结合?我的解决方法是:
1、流程类型约定业务对象类型(即此流程可以与哪种业务对象关联)
2、业务对象中的属性或其组合可以定义为流转条件(即实现业务对象信息影响工作流流转)
3、活动执行者可以选择业务系统的组织机构、角色、人员定义(我是通过接口方式进行约定,IOC注入)
4、活动可调用已定义的应用(可多个)
5、业务对象可以通过报批动作启动工作流实例,之后由工作流按照设计信息与业务信息进行自动流转或全程提供表单与审批按钮支持。
由于此工作流系统是出于简化的目的进行设计的,设计时与运行时信息我都使用数据库信息来表示,设计器也未提供拖拉界面来进行设计,而是采用顺序定义的方式来操作。在实际运作过程中,我觉得它可以满足一般规模不大(文职人员100人左右)的企业的OA应用。
本人原创文章,欢迎转载,转载请注明出处!
工作流引擎产品无论国内或国外都有不少成熟之作,开源的工作流产品也有诸如shark之类的精品。但工作流产品做为一个独立的中间件,无论是其本身或通过它进行流程设计及与你自己的系统整合,对很多使用过工作流产品的开发人员来说都是一件不容易的事。特别是在一些其实只是一些很简单的流程控制应用需要时,我们是否需要一个独立的工作流产品来运作呢?
也许你可以尝试自己做一个工作流组件?听起来好象有点儿难,但其实并不是一件很恐怖的工作。让我们先从通常会使用到工作流引擎的情景分析下我们需要什么?
情景:某公司需要对员工请假进行管理,员工请假需进行系统填写申请,如果请假天数<=1天,可以部门主管批准。如果请假天数>1天,需由部门主管->副总经理进行再行批复。批准后的请假自动记入考勤系统。
从这个简单的业务需求,我们进行分析它的需求:
1、工作流程的选择是由业务信息(请假单)相关联的,工作流独立存在是无意义的。
2、业务信息中的内容会决定流程的选择与流向。如:请假天数,或是主管的批复意见
3、工作流程的流转与组织结构、角色、员工相关。
4、工作流程通常会调用相关业务应用(记入考勤)来完成多应用系统之间的协作。
结合以上需求,我们定义出工作流系统所需功能与数据:
1、流程定义工具(负责生成工作流引擎能明白的流程控制信息),对应于XPDL
2、工作流控制变量定义(即用于控制流程流转的控制量,如请假天数与各级审批意见,可由系统根据流程实体信息自动注入至工作流引擎)
3、工作流相关数据,即与业务过程相关的数据,如:业务表单、组织结构、角色、员工等
4、工作流引擎,负责解释流程定义,创建过程实例并控制其执行,并可能提供相关的监控界面以保障工作流的正确运转。
5、外部应用,可由工作流引擎进行调用完成多个业务系统的流程衔接。这通常是工作流引擎的最大亮点。
未完待续>>
本人原创文章,欢迎转载,转载请注明出处!
* 如何在minilang中使用Java静态方法获得数据
使用beanshell脚本:
<set field="notApplied" value="${bsh:org.ofbiz.accounting.invoice.InvoiceWorker.getInvoiceNotApplied(invoice)}" type="Double"/>
警告:你必须使用type=""来转换你的结果类型,否则的话,它将返回字符串类型.
* 如何在minilang中调用Java程序
你可以在minilang中插入一段beanshell代码,类似于 applications/ecommerce/script/org/ofbiz/ecommerce/customer/CustomerEvents.xml的示例:
<call-bsh><![CDATA[
String password = (String) userLoginContext.get("currentPassword");
String confirmPassword = (String) userLoginContext.get("currentPasswordVerify");
String passwordHint = (String) userLoginContext.get("passwordHint");
org.ofbiz.securityext.login.LoginServices.checkNewPassword(newUserLogin, null, password, confirmPassword, passwordHint, error_list, true, locale);
]]></call-bsh>
在beanshell脚本中可以访问在minilang中所有的变量
* 清除 与 刷新的比较
<clear-field field-name="foo"/> 设置它为null. 这可以是一个类的属性或是集合中的一个值
<refresh-value value-name="foo"/> 从数据库中重新获得foo的值. foo必须是一个GenericValue.
* 如何设置一个布尔值
我无法找到任何例子,但是我这样做是成功的:
<set field="orderAvailableCtx.countNewReturnItems" value="true" type="Boolean"/>
我想minilang使用type=""中的类型与value中的值的做为构造调用.猜想在某天我看到这些代码时能证明我是对的...
本文档译自ofbiz 4.0 cookbooks,本人翻译,欢迎转载,请注明出处.
我的产品是被要求运行在多种常见数据库平台下(mysql/sqlserver/oracle)下,在开发中需要严格遵循相关的规范以确保能够实现跨数据库类型的要求.(相关的要点在我的"你的系统真的因为使用hibernate就可以适应各种数据库吗? "一文中已提及).在初始开发时有一个问题是比较困扰我的团队的,我们开发的时候必定是基于某个特定的数据库开发的(比如mysql),但在测试阶段是需要在不同的数据库平台下进行兼容性测试,由于开发过程中数据库结构与种子数据变化非常快,全部编写sql方式非常浪费时间,如何能找到一种高效的数据库相互迁移的工具,是我们当时所急需的解决方案.
其实也没啥选择,比较常用的数据库迁移工具就是Sqlserver自带的DTS,这玩意在sql server数据库间进行数据导入/导出时倒确实比较好用,在不同数据库类型进行操作时,就会出多多问题,如:类型转换需手工指定/导出字段有双引号...
所以最后的选择就是自己做一个DTS好啦,思路如下:
1、选择源数据库连接与目标数据库连接
2、根据源数据库遍历所有数据库对象(表),做为基准
3、删除目标数据库所有表外键及索引、删除所有种字数据(根据约定)数据、字段均允许null
4、遍历源数据库中所有表,为目标数据库修改结构(如增删字段,字段改类型、大小)
5、将源数据库中种子数据表数据拷贝至目标数据库中
6、根据源数据库为目标数据库中的表创建外键及索引、设置是否允许为null
7、搞掂!
完工后总代码量不过两千行(因为需考虑不同数据库的SQL Dialet,否则应该更少)
用户界面基于Eclipse RCP技术开发,使用JFace Wizard向导(如果不是想用向导的话,你可以用SWT来做)对话框获得源数据库与目标数据库的连接内容,并在用户点击完成按钮后,在进度条中提示用户执行情况.用了这个玩意以后,测试同事的数据库兼容性测试就再也不用来烦我们开发组啦!真是爽呀!当然很多喜欢折腾的客户(比如突然在哪里听说oracle是大型数据库,非让你帮他弄过去)此类朝三暮四也就自然不在话下啦!



本人原创文章,欢迎转载,转载请注明出处!
* 如何为SELECT SUM(QUANTITY - CANCEL_QUANTITY) AS QUANTITY之类的语句设置别名
<alias entity-alias="OI" name="quantity" function="sum">
<complex-alias operator="-">
<complex-alias-field entity-alias="OI" field="quantity" default-value="0"/>
<complex-alias-field entity-alias="OI" field="cancelQuantity" default-value="0"/>
</complex-alias>
</alias>
SELECT SUM(COALESCE(OI.QUANTITY,'0') - COALESCE(0I.CANCEL_QUANTITY)) AS QUANTITY在结果集包含默认值是一个好的习惯,否则如果有一项为null,那么最终相减的结果就也为null了.
操作符可以为任何你当前使用数据库所支持的SQL操作符,比如算术运算符+, -, * 和/ 或者字符串连接符 ||.
你可以增加function=""标签来完成在complex-alias-field中的min, max, sum, avg, count, count-distinct, upper 及lower集合运算. 示例, 以上的定义可以用另一种方法表示为:
<alias entity-alias="OI" name="quantity">
<complex-alias operator="-">
<complex-alias-field entity-alias="OI" field="quantity" default-value="0" function="sum"/>
<complex-alias-field entity-alias="OI" field="cancelQuantity" default-value="0" function="sum"/>
</complex-alias>
</alias>
即为SELECT (SUM(COALESCE(OI.QUANTITY,'0')) - SUM(COALESCE(OI.CANCEL_QUANTITY,'0'))) AS QUANTITY查询结果集
* 我讨厌OFBiz的实体引擎,我要自己的JDBC连接!
好的,以下是你获得JDBC连接的方法:
import org.ofbiz.entity.jdbc.ConnectionFactory;

String helperName = delegator.getGroupHelperName("org.ofbiz"); // gets the helper (localderby, localmysql, localpostgres, etc.) for your entity group org.ofbiz
Connection conn = ConnectionFactory.getConnection(helperName);
Statement statement = conn.createStatement();
statement.execute("SELECT * FROM PARTY");
ResultSet results = statement.getResultSet();

// 通过普通JDBC 的结果集来操作

//Alternatively, you can use the SQLProcessor like this:

SQLProcessor sqlproc = new SQLProcessor(helperName);
sqlproc.prepareStatement("SELECT * FROM PARTY");
ResultSet rs1 = sqlproc.executeQuery();

ResultSet rs2 = sqlproc.executeQuery("SELECT * FROM PRODUCT");

你可以查看framework/webtools/webapp/webtools/WEB-INF/actions/entity/EntitySQLProcessor.bsh了解它的使用
在以下网址你可以获得相关JavaDoc的内容:
http://www.opentaps.org/javadocs/version-1.0/framework/api/org/ofbiz/entity/jdbc/SQLProcessor.html
http://www.opentaps.org/javadocs/version-1.0/framework/api/org/ofbiz/entity/jdbc/ConnectionFactory.html
*** 请先考虑以下内容: 你放弃数据库的无关性意味着你在某些方法将无法与框架或其它程序集成.你确定你要这么做吗?
获得更好的做法,请访问 http://www.opentaps.org/docs/index.php/Using_the_Query_Tool
* 关于时间比较方法的一些警告
在你用 GREATER_THAN比较一个 Timestamp类型数据时, 你有可能获得相同的时间数据:
delegator.findByAnd("XXX", UtilMisc.toList(new EntityExpr("fromDate", EntityOperator.GREATER_THAN, "2007-12-31 23:59:59.998")));
有可能包含fromDate=2007-12-31 23:59:59.998的数据. (此种情况发生于PostgreSQL 8.1并且GenericDAO 类生成的SQL代码是'FROM_DATE > ' so 所以我也不明白发生这个问题的原因.) 所以确保安全的方法是, 增加1秒到需要比较的时间中然后使用 GREATER_THAN_EQUAL_TO方法
delegator.findByAnd("XXX", UtilMisc.toList(new EntityExpr("fromDate", EntityOperator.GREATER_THAN_EQUAL_TO, "2008-01-01 00:00:00.998")));
* 警告: 在空集合中使用EntityOperator.IN
请小心如果使用EntityOperator.IN去判断一个非空集合在一个空集合中的包含项,你有可能获得一个语法错误: 在Derby或者其它一些不为人知的数据库中可能会出错.
所以建议你能在使用EntityOperator.IN之间,通常执行UtilValidate.isNotEmpty方法来判断一下结果集是否为空
* 警告: delegator.getNextSubSeqId 不能确保唯一性
很多实体有很多合成的主键.示例OrderItem's 主键是orderId + orderItemSeqId. InventoryItemDetail's 主键是inventoryItemId +inventoryItemSeqId. 通常, delegator.getNextSubSeqId 通常是获得一个序列值,但是在多线程的访问下有可能无法确保生成的值的唯一性. 此段内容在 http://issues.apache.org/jira/browse/OFBIZ-1636 中有相关文档记录.
当前, 如果有可能多个线程尝试同时写入实体组合键时,可以使用delegator.getNextSeqId来替代getNextSubSeqId. (此问题不会发生于OrderItem, 因为它只使用单线程写入, 但有可能发生于 InventoryItemDetail, 它使用多线程来创建库存记录项.)
完>>
本文档译自ofbiz 4.0 cookbooks,本人翻译,欢迎转载,请注明出处.
* 我可以在entitymodel.xml文件中定义自己的view-entities吗?
不能, 你可以动态定义它们.你可以查看org.ofbiz.party.party.PartyServices中的findParty方法学习它的使用
* 如果为有效期间创建条件?
我们提供了一组非常有用的方法EntityUtil.getFilterByDateExpr ,它能返回一个EntityConditionList根据有效期间来筛选一个结果集.
* 如何在大数据结果集下工作
如果你检出一个大的数据结果集,你应当使用EntityListIterator通过迭代方式读取数据,而非List.
示例,如果你使用:
List products = delegator.findAll("Product");
你可能获得一个"java.lang.OutOfMemoryError". 这是由于你通过findAll, findByAnd, findByCondition等方法来获得一个大的内存数据结果集导致内存溢出. 在这种情况下, 应该使用EntityListIterator迭代方式来读取你的数据. 这个示例应改写成:
productsELI = delegator.findListIteratorByCondition("Product", new EntityExpr("productId", EntityOperator.NOT_EQUAL, null), UtilMisc.toList("productId"), null);
注意获得EntityListIterator的方法只用通过条件, 所以你需要将你的条件重写为EntityExpr (在此次情况下,productId是主键字段不可能为空的, 所以将返回所有Proudct实例,)或 EntityConditionList.
此方法参数中包含检出的字段(这里为productId)以及排序字段(这里不需要,所以赋了null)
你可以传递一个null作为EntityCondition参数来获得所有结果.然后这不一定在所有数据库下都能正常工作! 在maxdb及其它不常用的数据库下时你要小心使用这些高级功能.
* 如何使用EntityListIterator
当我们通过EntityListIterator迭代访问数据时, 通常是这样:
 while ((nextProduct = productsELI.next()) != null) {
 .
// operations on nextProduct
}
在EntityListIterator 中使用 .hasNext()方法是一种不经济的做法.
在你完成你的操作后,要记得关闭此迭代
productsELI.close();
* 如何查询无重结果集
当前只能通过list iterator方法并指定EntityFindOptions参数,示例如下:
listIt = delegator.findListIteratorByCondition(entityName, findConditions,
null, // EntityConditions参数
fieldsToSelectList,
fieldsToOrderByList,
//关键部分. 第一个true表示"specifyTypeAndConcur"
// 第二个true指完是一个滤重查询. 显然在实体引擎中只能通过这个方法来进行滤重查询
new EntityFindOptions(true, EntityFindOptions.TYPE_SCROLL_INSENSITIVE, EntityFindOptions.CONCUR_READ_ONLY, true));
在minilang, 它会更简单:
<entity-condition entity-name="${entityName}" list-name="${resultList}" distinct="true">
<select field="${fieldName}"/>
.
* 如何进行一个大小写不敏感的查询(即不分大小写)
你需要查询条件表达式两边均转为大写,示例:
andExprs.add(new EntityExpr("lastName", true, EntityOperator.LIKE, "%"+lastName+"%", true));
(来源org.ofbiz.party.party.PartyServices)
* 如何将EntityListIterator转换成List
使用EntityListIterator.getCompleteList() 及getPartialList 方法
* 如何自动获得下一个ID值
在minilang 中使用 <sequence-id-to-env ...> 或在Java中通过delegator.getNextSeqId(...) 获得 . id序列存放于SequenceValueItem中.
* 关于ID值的一些警告
不要在种子/演示数据中使用10000做为数据的ID,当系统尝试自动创建数据时,它们都将尝试10000,这将导致一个键值冲突错误.
* 如何从一个明细项中获得序列ID
有些实体,比如拥有itemSeqId 的InvoiceItem(发票明细项) and OrderItem(订单明细项).此项通常在你处一次为item生成GenericValue 时自动生成ID,之后向delegator要求生成项目的seq Id:
GenericValue orderItem = delegator.makeValue("OrderItem", orderItemValues);
delegator.setNextSubSeqId(orderItem, "orderItemSeqId", ORDER_ITEM_PADDING, 1);
未完待续>>
本文档译自ofbiz 4.0 cookbooks,本人翻译,欢迎转载,请注明出处.
|