个人对于2019年企业数据与营销的思考

目前存在的绝大部分企业以后要么专注于生产,要么专注于落地服务(例如餐厅,连锁店),数据、营销只能外包。数据整合包括三部分:1)企业自己各个独立系统之间的数据整合,例如OA与ERP与HRM之类的企业自己内部系统之间数据整合;2)企业与企业间外部系统之间数据采集、清洗,整合;例如我现在做的超市数据与企业内部ERP之间数据整合;3)数据自身之间的整合,例如企业内部生产、批发(B2B)数据与外部销售数据(B2C)之间的整合,从而形成自己独特BI。

营销之所以发微博、做直通车、朋友圈转发、做百度推广无效是因为IP基础不存在,无品牌,所以无法落地,当然则是传统营销弊端。IP营销显然超出了绝大部分企业的能力;即使有IP品牌影响力企业也因为碎片化流量造成营销渠道太多而超出自身能力,所以也只能外包,自身专注在设计和生产。

目前IP红利是抖音,草根也能快速品牌化,流量化,快手的草根性显然距离个人IP比较远

低代码开发在企业软件开发中的应用技巧4:再谈低代码开发与快速开发平台的关系

在上一篇 低代码开发在企业软件开发中的应用技巧3:低代码开发与快速开发平台的关系中我说到:快速开发平台的核心还是开发,低代码开发的理念是不开发或者少开发; 快速开发平台的使用对象是程序员,低代码开发平台的使用对象是业务员或者产品经理, 这是两者最大的区别。

昨天我给一个朋友的公司灌输这个理念,老板听了还比较感兴趣,低代码,低成本。跟对方公司负责技术的人聊,该 负责技术的人也听懂了,无法反驳,最后说这种技术只能运用在小公司,上不了档次。我心中呵呵,如果是美团、淘宝这样的互联网公司的业务量,每天几千万订单或者数亿订单,我如果拿出这种低代码的解决方案,我还不如一头撞死,我也丢不起这个人啊。

低代码解决方案的客户群是企业客户,特点如下:1)业务访问量不大,一家企业,即使是集团企业,一块业务,肯定在几万人同时使用以内,即使我自己做平台,做SaaS的,理论上有百万、千万人访问,我也会根据不同的企业,转到不同的服务器上,肯定不会在一台机器上吊死。在一台机器上,会严格控制人数,所以性能不是问题;2)低代码开发针对的是独立功能模块的企业应用,解决单一问题,例如:工单系统,特定需求的报表查询,简单进销存,自定义特殊需求BI,而不是复杂庞大的ERP;

朋友公司本来规模不大,百人以内,系统自用或者给合作伙伴用,理论上是SaaS的,但就目前使用量来说,不会超过千人。所以说自己就是一个简单应用,说低代码开发只适合小公司,是没认清自己的形式。

对于一个程序员来讲,将来的出路,最直接的无非三个:

1)靠近管理,逐步升级,提高自己的管理能力,原来管3个人,逐步能管30人,300人,3000人,3万人,这种能力是稀缺的;

2)靠近技术,市面上流行什么,猛钻研进去,形成自己核心竞争力;可绝大部分程序员估计都是这么想的,可惜的是,99.99%的程序员都做不到这一点,真正的猛钻研,绝大部分只是掌握了皮毛,会说一些前沿名词,自己动手去做的时候,我又分为三大类人:(A)会按照Read Me或者Help搭建环境,但是解决不了任何实际问题(占70%以上);(B)能把该前沿技术应用到实际项目中,能填一些实际的坑 (占20%),应该是公司核心主力;(C)全面超越开源技术,例如各大厂的核心负责人,见过猪跑,杀过猪,吃过猪肉(占10%以内) ;所以说,绝大部分程序员口中所谈的前沿技术,只是皮毛而已,这时候,就得靠嘴巴了,看谁会向老板汇报,这时实际就是靠情商了;

3)贴近业务,技术就是为业务服务的,保证质量,及时响应,快速满足业务需求,最后是一个技术+业务的专家。 贴近业务,实际就是最贴近钱。我自己的目标以及低代码开发平台的理念就是贴近业务。

低代码开发在企业软件开发中的应用技巧3:低代码开发与快速开发平台的关系

无聊的时候,常去逛oschina.net 上逛,oschina.net 有个码云(https://gitee.com)的项目,类似于github.com ,我观察 码云上的企业应用,有很多是快速开发平台性质的项目,例如:JFinal、Gun、Jeesite 等等,比较奇葩的是 JFinal,企业后台在Spring Boot一统江湖的今天,JFinal 也能一枝独秀,处处能看到其身影,无论是B2C的网上商城,还是微信小程序,还是其他企业应用,都能看到基于JFinal的应用,没用过,也不知道其奥秘在哪里。 这些快速开发平台往往侧重于Admin后台管理,例如,用户注册、角色、权限、菜单,一般是基于Spring Boot/Spring MVC+Hibernate/Mybatis+Spring Security/Shiro等组合,对于企业应用,都有一些自己的特色。不过,快速开发平台与低代码开发平台还是完全不同的两回事。

一、面向受众不同,即客户不同。快速开发平台的受众(客户)是程序员,程序员基于这些平台做二次开发,比如:用户权限等都是现成的,所以往往能有效提高项目的交付速度,能给程序员节省不少时间放在企业逻辑的梳理和开发上,但是核心还是源代码的开发。而低代码开发平台的受众是实施人员(不是程序员), 低代码开发平台的目标是任何需求都实施出来(理论上),而不是开发出来,国内比较典型的例如泛微,通过自己的实施平台,到处做20-30万项目,一个实施人员做一个月,今年拿到投资人钱的还有搭搭云等等,国内做类似平台的还有好几家,国外例如微软也投入到了这个市场上。对于Salesforce和SAP,其产品核心交付人员也是实施人员而不是程序员,当然, Salesforce和SAP实施人员的工资很高,甚至比程序员还高,达不到降低费用的目的,这是另外一个话题。

二、理念不同,快速开发平台的核心还是开发,总比不过提供了很多快速开发工具,适应客户需求的多变,是源代码级的交付。不过代码写的越多,即使是自动生成出来的,理论上Bug也不少。而实施平台交付的是产品和基于这个产品上搭建的模型,新开发的代码量是很少的,往往是模型驱动。

三、低代码平台的核心目标是快速交付,降低客户费用,使得客户能够快速看到原型,而快速开发平台的目标理论上也是差不多的这个目标,但是由于底层价值观完全不一样,所以实现方式上也完全不同, 快速开发平台由于还是程序员+开发模式,交付风险并不能有效降低。程序员对业务的理解不可能有实施人员理解的那么深入,虽然快速做出来了,但是很可能不是用户想要的。

低代码开发在企业软件开发中的应用技巧2:忘记O/R Mapping

还是在那个大厂做项目的过程当中,甲方架构师力推Hibernate/JPA,极力反对MyBatis,在这里,我并不想比较JPA与MyBatis的孰优孰劣,这种低层次的比较,就跟比较Java、.Net、PHP、Python、React、VUE等语言孰优孰劣一样,离开使用上下文,说哪个语言是最牛B的语言,只能说自己too native,too young。

就低代码开发而言,我真的不喜欢JPA,就拿最常用的Excel数据上传来讲,Excel表格数据上传,最常用的就是POI,但是POI有个问题,就是性能不佳,Excel数据超过30万,就会内存溢出,虽然也有很多别的技巧,例如采用异步调用等等,但总是有各种各样的问题。所以寻来寻去,就看到了阿里巴巴的开源项目,easyexcel,下载后试用了一下,性能确实就如其宣称的那样强劲,也宣称支持百万条记录以上,所以非常满意。但是将其应用到项目当中时,发现 easyexcel 有个很大的问题,就是其接口支持的形式是:

List data = EasyExcelFactory.read(is, new Sheet(1, 2, Xhd.class));

那个Xhd.class就是自定义的ValueObject或者Entity实体类,跟表结构一致或者自定义的值对象或者O/R Mapping对象,为啥我说低代码开发在企业软件开发中的应用要忘记O/R Mapping呢?企业应用开发,往往表结构很多,很复杂或者很随意,比较动态,虽然针对某个企业来讲,表结构设计好后,很少会在大规模改变,但是代码在从项目往产品转变,或者在做下一个客户的下一个项目时,表结构就千差万别,完全不一样了。如果这里绑定死了某个Entity的名字,那么下一个项目该段代码就得完全重新开发,那就不是低代码开发了,就是完全订制化了。

而使用MyBatis,我们完全可以动态注入SQL,而不是绑定死表结构或者绑定死O/R Mapping对象。虽然 O/R Mapping对象我们也可以采用生成器之类的工具虽然生成这些对象,但是我确实也不喜欢纯粹为了O/R Mapping而 生成一大堆无意义的Entity。 也许Hibernate/JPA也支持动态注入 SQL而我不知道,即使真的支持,这也与 Hibernate/JPA=O/R Mapping工具的初衷相违背,失去了初心,使用它就是其门绝技了,不值得提倡。

低代码开发在企业软件开发中的应用技巧:开篇

企业软件追求的是性价比,即在预定时间内保质按时完成,而不是代码质量高。这里的保质是业务使用上无Bug,性能满足用户平时工作要求。这里并不是暗示程序员可以随意写烂代码,不讲究架构,而是优先级让位于开篇讲的保质按时完成。

我曾在某大厂与TW同事一起参与某个项目的短暂开发,在这里,我并不想做戳穿TW的敏捷开发无用论的皇帝新衣的那个小男孩,事实上,短暂的与TW架构师一起合作开发,还是学到了敏捷开发的理念并深以为有用,不过,在实践中过分强调技术上的代码质量和代码技巧,个人并不认同。个人的理念是在企业软件尤其是业务相关的管理类软件,应该采用低代码开发技巧而不是强调敏捷开发或者高质量代码开发技巧。当然,如果团队成员素质普遍较高,资金、时间又比较充裕,严格要求代码质量,采用敏捷开发过程,对团队和交付都是有好处的。不过,企业软件开发的现状绝大部分都是时间紧迫,预算少,成员普遍工作经验少,或者没有在大公司和互联网公司工作过。这种情况下,再强调代码技巧和质量项目很可能就做不下去了。

当然,开发过程中代码质量如果真的比较低,对客户,对参与开发项目的企业、成员也是极大的损失和浪费时间、浪费金钱,也是不能接受的。所以,这里强调的是如何使用低代码开发(不是低质量)技巧在预算有限,成员素质参差不齐情况下,按时保质完成任务。

String boot @Scheduled和数据库连接池的 APPARENT DEADLOCK各种异常

一般来说,采用Spring Boot+MyBatis,无论是使用c3p0这样的老牌数据库连接池,还是使用Druid这样的阿里出品的数据库连接池,一般都不会有问题。我用Spring Boot做了一个基于JWT统一登录的微服务,采用的就是c3p0,几个月不登陆,也没出现数据库 APPARENT DEADLOCK!!! Complete Status:,No operations allowed after connection closed等等这样的异常,但是使用了@Scheduled后,数据库连接池频繁出现上述两种异常,天天死给我看,搞得我非常不爽,我想肯定是@Scheduled的问题,但是究竟出了什么问题?网上搜索了一阵资料后,大致明白了其中道理。

1)无论是APPARENT DEADLOCK!!!还是No operations allowed after connection closed都是数据库连接池超时或者使用了已经关闭的连接池造成的。那么为什么会出现这样的问题呢?

A)数据库连接池也是客户端/服务器端两端通信的问题,要么客户端连接服务器时,服务器无响应(例如mysql8小时的问题,mysql数据库把来自客户端的请求关闭了),要么

B) 是客户端自身本身的Connection 已经关闭了,但是新的Object Instance 还是使用了该Connection,则就出错了,这时可通过配置

<property name=”maxPoolSize” value=”300″ />
<property name=”minPoolSize” value=”10″ />
<property name=”maxIdleTime” value=”1800″ />
<property name=”maxStatements” value=”0″ />
<property name=”maxStatementsPerConnection” value=”100″/>
<property name=”initialPoolSize” value=”10″ />
<property name=”idleConnectionTestPeriod” value=”1800″ />
<property name=”testConnectionOnCheckin” value=”true” />
<property name=”automaticTestTable” value=”Test” />
<property name=”testConnectionOnCheckout” value=”false” />
<property name=”breakAfterAcquireFailure” value=”false” />

调整这些参数解决,Connection 即该关闭的及时关闭,不要长时间打开不关

C)Spring Boot @Scheduled的坑,@Scheduled长期打开的是同一个Class的同一个Object Instance,即Java的 JVM并没有把该Object销毁并进行内存回收,那么同一个Object Instance—–>Mybatis –>JDBC–>Database获取数据库连接池的路径,C3P0还是返回原来的那老一个,但是那老一个早已经被销毁,所以就会出现 APPARENT DEADLOCK!!!的问题。

解决的办法要么通过Http Client请求该定时器,例如采用Quartz或者Elstic JOB等外部中间件,Http调用每次都会生成一个新的线程,新的线程会生成新的Object,这时新的Object 从C3P0数据库连接池获取Connection时,就会返回新的Connection

要么定制一下Spring @Scheduled源代码(待续)

JDK11把JAVA EE部分删除了

JavaEE由4部分组成:

JAX-WS (Java API for XML-Based Web Services),
JAXB (Java Architecture for XML Binding)
JAF (the JavaBeans Activation Framework)
Common Annotations.
但是这个特性和JavaSE关系不大。并且JavaEE被维护在Github(https://github.com/javaee)中,版本同步造成维护困难。最后,JavaEE可以单独引用,maven中心仓库也提供了JavaEE(http://mvnrepository.com/artifact/javax/javaee-api/8.0),所以没必要把JavaEE包含到JavaSE中。

 

做了这么多年Java开发,不知道JDK中包含的jAVA EE分4部分,脸红。。。

https://mp.weixin.qq.com/s/Yz8V3inWN8uErKM-IdMFWQ

 

关于React中动态render String的React元素

由于产品需要,React的Form元素是动态生成的,虽然阿里的antd给了动态生成Form的demo https://ant.design/components/form-cn/#components-form-demo-dynamic-form-item ,事实上,我也按照这个Demo把动态生成Form 表单的特色加入到了自己的产品里。

对于简单的动态表单生成,这个feature也足够了,但是对于稍微复杂的表单布局,如下图所示:

 

表单的字段1)要分组 2)一行排2个字段以上,用阿里的antd就比较困难。而且这些字手段是从配置信息动态读取出来的。

这些字手段是从配置信息动态读取出来是个字符串,如何把这些字符串在React动态呈现出来?

http://w3cgeek.com/inject-jsx-formatted-string-into-react-component.html 这里讲了两种String 转换为JSX的方法。简单的String/Html转JSX还可以,但是带第三方自定义函数的,又无法转了。

我最后放弃了String转React的JSX Component的想法,直接在服务器端生成String类型的HTML,直接用React的dangerouslySetInnerHTML 奇门绝技,在服务器端动态生成HTML的动态表单,用React的dangerouslySetInnerHTML 呈现。

 

让HtmlUnit忽略执行某个JavaScript文件

开始是想在HtmlUnit执行完一些JS文件后获取对应URL的HTML页面,但是期间发生阻塞。。。

怀疑是HtmlUnit在执行某个JS文件时陷入了无限循环中,检查是哪个JS文件造成的,如果该JS文件没有太大的价值就可以让HtmlUnit忽略它不执行它:
新建一个类:
class InterceptWebConnection extends FalsifyingWebConnection{
    public InterceptWebConnection(WebClient webClient) throws IllegalArgumentException{
        super(webClient);
    }
    @Override
    public WebResponse getResponse(WebRequest request) throws IOException {
        WebResponse response=super.getResponse(request);
        if(response.getWebRequest().getUrl().toString().endsWith("dom-drag.js")){
            return createWebResponse(response.getWebRequest(), "", "application/javascript", 200, "Ok");
        }
        return super.getResponse(request);
    }
}
然后创建你自己的webClient:
new InterceptWebConnection(webClient);