文章标签 ‘nutz’
202411 月29

Java :请求Https地址,忽略证书

以下以Nutz代码为例:

Header header = Header.create();
        header.addv("Content-Type", "application/json");
        header.addv("AppCode", appcode);
        Request request = Request.create(url, Request.METHOD.POST);
        NutMap body = NutMap.NEW();
        body.put("kssj", kssj);
        body.put("jssj", jssj);
        request.setHeader(header);
        request.setData(Json.toJson(body));
        Sender sender = Sender.create(request).setTimeout(30 * 1000);
        if (url.startsWith("https")) {
            try {
                SSLContext sslcontext = this.createIgnoreVerifySSL();
                sender.setSSLSocketFactory(sslcontext.getSocketFactory());
                sender.setHostnameVerifier((urlHostName, session) -> true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        Response response = sender.send();
        if (response.isOK()) {
            //todo
        }

private static class TrustAllManager
implements X509TrustManager {
public X509Certificate[] getAcceptedIssuers() {
return null;
}

public void checkServerTrusted(X509Certificate[] certs,
String authType) {
}

public void checkClientTrusted(X509Certificate[] certs,
String authType) {
}
}


public SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{new CityLifelineServer.TrustAllManager()}, null);
return sc;
}

20238 月10

Nutz Https请求忽略SSL证书

@IocBean
@Slf4j
public class CimApiServer {
    @Inject
    private RedisService redisService;
    private String redis_key = "cim:accessToken";
    @Inject
    @Reference(check = false)
    private ISysConfigProvider sysConfigProvider;

    public String getAccessToken() {
        String token = redisService.get(redis_key);
        if (Strings.isBlank(token)) {
            token = this.getHttpToken();
            redisService.setex(redis_key, 3600 * 24 - 100, token);
        }
        return token;
    }

    private String getHttpToken() {
        String CIM_GIS_APPID = sysConfigProvider.getString("COMMON", "CIM_GIS_APPID");
        String CIM_GIS_HTTP_BASE = sysConfigProvider.getString("COMMON", "CIM_GIS_HTTP_BASE");
        String CIM_GIS_APPKEY = sysConfigProvider.getString("COMMON", "CIM_GIS_APPKEY");
        String CIM_GIS_APPSECRET = sysConfigProvider.getString("COMMON", "CIM_GIS_APPSECRET");
        Map<String, Object> params = new HashMap<>();
        params.put("apiKey", CIM_GIS_APPKEY);
        params.put("secret", CIM_GIS_APPSECRET);
        Header header = Header.create();
        header.addv("Content-Type", "application/json");
        Request request = Request.create(CIM_GIS_HTTP_BASE + "/auth/getAccessToken", Request.METHOD.POST);
        request.setHeader(header);
        request.setData(Json.toJson(params));

        Sender sender = Sender.create(request).setTimeout(20 * 1000);
        if (CIM_GIS_HTTP_BASE.startsWith("https")) {
            try {
                SSLContext sslcontext = createIgnoreVerifySSL();
                sender.setSSLSocketFactory(sslcontext.getSocketFactory());
                sender.setHostnameVerifier((urlHostName, session) -> true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        Response response = sender.send();
        if (response.isOK()) {
            NutMap map = Json.fromJson(NutMap.class, response.getContent());
            log.debug("getHttpToken:::" + Json.toJson(map));
            if (0 == map.getInt("code")) {
                return map.getString("data");
            }
        }
        return "";
    }

    private static class TrustAllManager
            implements X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkServerTrusted(X509Certificate[] certs,
                                       String authType) {
        }

        public void checkClientTrusted(X509Certificate[] certs,
                                       String authType) {
        }
    }


    public SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, new TrustManager[]{new TrustAllManager()}, null);
        return sc;
    }
}
20198 月2

NutzWk 5.2.6 及 5.2.6-mini 发布,Java 微服务分布式开发框架

项目介绍:

NutzWk 是有五年多历史的Java Web开源开发框架,其5.x 是Java 微服务分布式版本,采用nutzboot(nutz核心)、dubbo、redis、zookeeper、shiro、quartz、beetl、logback、vue、sentinel(流控框架,可选)、seata(分布式事务,可选) 等开源技术的微服务分布式版本,自带系统管理、简易CMS、微信模块、定时任务、服务API等功能,目前已应用于全国各地上千个各类商业项目中。

演示地址(Vue版本): https://nutzwk.wizzer.cn

后端技术架构:nutzboot(国产,基于国产nutz) + dubbo(国产) + redis + zookeeper + shiro + quartz + logback 等主流技术
前端技术架构:vue+ element

NutzWk v5.2.6 更新内容:

  • 修改错误页机制,将前后台404/403/505 错误页严格区分;
  • 修改POJO类,引入 @PrevInsert 注解,在执行fastInsert数据快速入库时有效;
  • 修改web项目,Globals类 Map 对象改为 NutMap 方便开发;
  • 修改vue后台,系统管理中的页面弹出框,改为手动关闭;
  • 修复vue后台,数据字典管理的排序问题;
  • 其他功能的完善,vue页面优化等;
  • 相关jar包及第三方依赖的版本升级;

NutzWk v5.2.6-mini 版本发布:

  • 具有v5.2.6 全部功能的微服务mini版本(系统管理/CMS管理/微信管理/API接口等功能齐全);
  • 管理后台 vue + element;
  • 非分布式,一个jar包即可运行(无dubbo依赖、无需zookepper,只需安装数据库+redis);
  • 非常适合小型项目快速开发;
20186 月9

NutzWk 5.0.x 微服务分布式版本开发及部署说明

NutzWk 5.x 已发布一段时间,这段时间基于此版本开发了智慧水务系统(NB-IOT)、某物联网平台、某设备租赁平台、某智慧睡眠平台、某智慧园区项目等,开发和部署过程中遇到一些小问题,开这个帖子把一些经验分享出来省的大家走弯路。

项目地址1: https://github.com/Wizzercn/NutzWk
项目地址2: https://gitee.com/wizzer/NutzWk

1、运行环境
其实项目readme和wk-wiki 已经写的很清楚了,在此强调一下,不是说非这些版本不可,但对于新手来说最好版本号保持一致,能跑起来了您再折腾玩~~

JDK 8 162 +
Maven 3.5.3 +
Redis 4.0.8 +
MySql 5.7 +
Zookeeper 3.4.11 +

2、开发环境
一般建议使用IDEA进行开发,因为是maven多模块的项目,直接用IDEA打开项目根目录,它会通过maven下载jar包,自动构建项目
然后如何启动项目呢,有很多种方式,简单说几个:
1)打开每个NB项目(nutzboot简称)项目里的main类,右击运行,例如 cn.wizzer.sys.commons.core.***MainLauncher
2)通过IDEA 的Run 配置 Application 运行,详见 https://github.com/Wizzercn/NutzWk/blob/nutzboot-dubbo/wk-wiki/01.QuickStart/01.02.Start.md
3)命令行在NB项目根目录运行mvn compile nutzboot:run 或者IDEA右侧Maven管理界面里通过插件运行,,详见 https://github.com/nutzam/nutzboot-maven-plugin

3、启动顺序
保证MySQL、Redis、Zookeeper 都正常启动且为默认端口及默认配置(当然这些配置项可以在application.properties 修改的)
1)MySQL创建一个空白数据库,编码格式为UTF-8,数据库名称 nutzwk_nb
2)NB项目的模块启动顺序是 sys –> cms[可选] –> wx[可选] –> task[可选] –> web-platform –> web-api[可选]
3)如上所述,如果想运行访问后台,只需要启动 sys 和 web-platform即可,注意是有启动顺序的,其他模块需要用就启
4)task 定时任务是依赖于sys的,而web-platform系统管理对定时任务管理是依赖于 task模块的,如果你想让task独立运行并且不需要通过页面进行管理,自己少做改动即可,不是不可以哦

4、部署注意事项
1)因为登录页面对密码进行了RSA加密,有时候部署会遇到怎么也登录不了,而后台抛异常 java.lang.SecurityException: JCE cannot authenticate the provider BC 的情况,解决方法在代码注释里已写明了,不过很少有人去看
https://github.com/Wizzercn/NutzWk/blob/nutzboot-dubbo/wk-app/wk-nb-web-platform/src/main/java/cn/wizzer/app/web/commons/shiro/filter/PlatformAuthenticationFilter.java

1、编辑文件 /usr/java/jdk1.8.0_162/jre/lib/security/java.security
     9下面添加 security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
2、拷贝 bcprov-jdk16-143.jar  bcprov-jdk15-135.jar  /usr/java/jdk1.8.0_162/jre/lib/ext 目录下
3、别问我上面两个文件怎么找……

(如果您是https的话可以把RSA加密方式改掉弃用哦)
2)服务器注意事项:服务器时间同步做没做、hosts里配没配主机名hostname和127.0.0.1的映射关系、内存够不够用(有没有给jar指定内存大小)等

5、其他
1)请关注 NutzWk 的动态,有新的版本发布建议及时更新,往往会修复问题或新增功能
2)如果 NutzWk 给了您帮助,或已用于生产, https://wizzer.cn/donation 欢迎打赏一定金额以资鼓励,创造国内良好的开源环境
3)最后感谢兽兽及nutz社区广大网友的帮助和鼓励,没有您们的支持,这个项目不会历经6年多还在更新前进

20182 月26

利用nutz+t-io实现硬件设备的socket通信

 

1、MainServer 启动类

/**
 * Created by Wizzer on 2018/2/26.
 */
@IocBean
public class MainServer {
    private static final Log log = Logs.get();

    public static void main(String[] args) {
        try {
            ComboIocLoader loader = new ComboIocLoader(
                    new String[]{"*json", "config/ioc/", "*anno", "cn.wizzer","*rabbitmq"}
            );
            NutIoc ioc = new NutIoc(loader);
            //socket
            ioc.get(SocketServer.class).init();
            //http
            ioc.get(HttpServer.class).init();
            //mq
            String topicQueue = "sweeper-tioTopicQueue";
            ConnectionFactory factory = ioc.get(ConnectionFactory.class, "rabbitmq_cf");
            Connection rabbitmq_conn = factory.newConnection();
            Channel rabbitmq_channel = rabbitmq_conn.createChannel();
            rabbitmq_channel.queueDeclare(topicQueue, true, false, false, null);
            rabbitmq_channel.exchangeDeclare("sweeper-tioTopicExchange", BuiltinExchangeType.TOPIC, true);
            rabbitmq_channel.queueBind(topicQueue, "sweeper-tioTopicExchange", "tio.#");


        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

2、SocketServer 数据接收

 

/**
 * Created by Wizzer on 2018/2/26.
 */
@IocBean
public class SocketServer {
    private static final Log log = Logs.get();
    //handler, 包括编码、解码、消息处理
    @Inject
    private MyServerAioHandler myServerAioHandler;
    //事件监听器,可以为null,但建议自己实现该接口,可以参考showcase了解些接口
    private ServerAioListener aioListener;
    //一组连接共用的上下文对象
    private ServerGroupContext serverGroupContext;
    //aioServer对象
    private AioServer aioServer;
    //有时候需要绑定ip,不需要则null
    private String serverIp;
    @Inject
    private PropertiesProxy conf;

    public void init() throws Exception {
        int port = conf.getInt("server.socket.port", 8600);
        log.debug("socket port::" + port);
        serverGroupContext = new ServerGroupContext("tio", myServerAioHandler, aioListener);
        serverGroupContext.setHeartbeatTimeout(30000);
        aioServer = new AioServer(serverGroupContext);
        aioServer.start(serverIp, port);
    }
}

3、socket 数据包的解析

4、RabbitMQ 队列+消费者 实现数据入库

5、HttpServer 提供HTTP API用于对设备发送命令

6、socket 命令包的下发

 

源码:https://gitee.com/wizzer/demo

20181 月21

Elasticsearch 6.1.2 (二)分页查询、排序、关键词查询,集合beetl实现前台展示

后台代码,自定义tag:

package cn.wizzer.app.web.modules.tags;

import cn.wizzer.app.web.commons.ex.elasticsearch.EsService;
import cn.wizzer.app.web.commons.utils.YcDateUtil;
import cn.wizzer.app.ycold.modules.services.YcoldInquiryService;
import cn.wizzer.framework.page.Pagination;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.beetl.core.GeneralVarTagBinding;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Created by wizzer on 2018/1/20.
 */
@IocBean
public class YcoldInquiryListTag extends GeneralVarTagBinding {
    private final static Log log = Logs.get();
    @Inject
    private EsService esService;
    @Inject
    private YcoldInquiryService ycoldInquiryService;
    @Inject
    private PropertiesProxy cfg;

    @Override
    public void render() {
        String startDate = Strings.sNull(this.getAttributeValue("startDate"));
        String endDate = Strings.sNull(this.getAttributeValue("endDate"));
        String keyword = Strings.sNull(this.getAttributeValue("keyword"));
        int pageNumber = NumberUtils.toInt(Strings.sNull(this.getAttributeValue("pageNumber")), 1);
        int pageSize = NumberUtils.toInt(Strings.sNull(this.getAttributeValue("pageSize")), 10);
        boolean highlight = BooleanUtils.toBoolean(Strings.sNull(this.getAttributeValue("highlight")));
        boolean explain = BooleanUtils.toBoolean(Strings.sNull(this.getAttributeValue("explain")));
        String sortName = Strings.sNull(this.getAttributeValue("sortName"));
        String sortOrder = Strings.sNull(this.getAttributeValue("sortOrder"));
        Pagination page = new Pagination();
        page.setPageNo(pageNumber);
        page.setPageSize(pageSize);
        try {
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            //根据名称查询
            if (Strings.isNotBlank(keyword)) {
                query.must(QueryBuilders.wildcardQuery("CASNM", "*" + keyword + "*"));
            }
            //截止时间大于等于现在
            query.must(QueryBuilders.rangeQuery("IQDAT").gte(Times.format("yyyyMMddHHmmss", new Date())));
            //公共日期起
            if (Strings.isNotBlank(startDate)) {
                query.must(QueryBuilders.rangeQuery("ANNODAT").gte(startDate.replaceAll("-","")));
            }
            //公共日期至
            if (Strings.isNotBlank(endDate)) {
                query.must(QueryBuilders.rangeQuery("ANNODAT").lte(endDate.replaceAll("-","")));
            }
            //几个状态条件
            query.must(QueryBuilders.matchQuery("BUYER_STS", "N"));//采购商状态
            query.must(QueryBuilders.matchQuery("STS", "A"));//状态
            query.must(QueryBuilders.matchQuery("ANNOMK", "Y"));//公告註記
            SearchRequestBuilder srb = esService.getClient().prepareSearch(cfg.get("es.index.name"))
                    .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                    .setTypes("inquiry")
                    .setQuery(query)
                    //分页
                    .setFrom((pageNumber - 1) * pageSize).setSize(pageSize)
                    //是否按匹配度排序
                    .setExplain(explain);

            if (highlight) {
                HighlightBuilder highlightBuilder = new HighlightBuilder().field("*").requireFieldMatch(false);
                highlightBuilder.preTags("<span style=\"color:red\">");
                highlightBuilder.postTags("</span>");
                srb.highlighter(highlightBuilder);
            }
            if (Strings.isNotBlank(sortName)) {
                String[] sortNames = StringUtils.split(sortName, ",");
                if ("asc".equalsIgnoreCase(sortOrder)) {
                    for (String s : sortNames) {
                        srb.addSort(s, SortOrder.ASC);
                    }
                } else {
                    for (String s : sortNames) {
                        srb.addSort(s, SortOrder.DESC);
                    }
                }
            }
            log.debug("srb:::\r\n" + srb.toString());

            SearchResponse response = srb.execute().actionGet();
            SearchHits hits = response.getHits();
            page.setTotalCount((int) hits.getTotalHits());
            List<Map<String, Object>> list = new ArrayList<>();
            hits.forEach(searchHit -> {
                Map<String, Object> source = searchHit.getSourceAsMap();
                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                //name高亮
                HighlightField nameField = highlightFields.get("CASNM");
                if (nameField != null) {
                    Text[] fragments = nameField.fragments();
                    String tmp = "";
                    for (Text text : fragments) {
                        tmp += text;
                    }
                    source.put("CASNM", tmp);
                }
                source.put("IQDAT", YcDateUtil.get_yyyyMMdd_HHmm(Strings.sNull(source.get("IQDAT"))));
                source.put("ANNODAT", YcDateUtil.get_yyyyMMdd(Strings.sNull(source.get("ANNODAT"))));
                list.add(source);
            });
            page.setList(list);
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.binds(page);
        this.doBodyRender();
    }
}

前台beetl页面:

  <#ycold_inquiry_list pageNumber="${pageNumber}" pageSize="${pageSize}" startDate="${startDate}" endDate="${endDate}" keyword="${keyword}" sortName="ANNODAT" sortOrder="desc" highlight="false" var="p">

    <table class="list_table_blue even_table">
        <thead>
        <tr>
            <th>公告日期</th>
            <th>案件名称</th>
            <th>交货地点</th>
            <th>报价截止日期</th>
            <th width="110">查看详细</th>
        </tr>
        </thead>
        <tbody>
        <%for(o in p.list){%>
        <tr>
            <td>${o.ANNODAT!}</td>
            <td class="l_text"><a href="${base!}/purchase/info/${o.XUID!}" target="_blank" class="td_a hide1">${o.CASNM!}</a>
            </td>
            <td class="l_text">${o.DLSITE!}</td>
            <td>${o.IQDAT!}</td>
            <td><a href="${base!}/purchase/info/${o.XUID!}" target="_blank" class="more_a png"></a></td>
        </tr>
        <%}%>
        </tbody>
    </table>
    <%if(p.totalCount>1){%>
    <div class="page round_s_a"></div>
    <script type="text/javascript">
        $(function () {
            $(".page").createPage({
                pageCount: ${p.totalPage},
                totalCount: ${p.totalCount},
                current: ${p.pageNo},
                backFn: function (p) {
                    window.location.href = "?page=" + p + "&size=${p.pageSize}";
                }
            });
        });
    </script>
    <%}%>
20181 月21

Elasticsearch 6.1.2 (一)中文分词设置、字符串字段排序设置

if (!esService.isExistsType(cfg.get("es.index.name"), type)) {
                //初始化索引表
                XContentBuilder mapping = jsonBuilder().startObject()
                        .startObject(type)
                        .startObject("_all")//设置IK分词
                        .field("analyzer", "ik_max_word")
                        .field("search_analyzer", "ik_max_word")
                        .field("term_vector", "no")
                        .field("store", "false")
                        .endObject()
                        .startObject("properties")
                        .startObject("CASNM").field("type", "text").field("analyzer", "ik_max_word").endObject()
                        .startObject("IQDAT").field("type", "text").field("index", "true").field("fielddata","true").endObject()
                        .startObject("ANNODAT").field("type", "text").field("index", "true").field("fielddata","true").endObject()
                        .endObject()
                        .endObject()
                        .endObject();
                esService.putMapping(cfg.get("es.index.name"), "inquiry", mapping);
            }

    /**
     * @param indexName 索引名
     * @param type      数据类型(表名)
     * @param mapping   mapping对象
     */
    public boolean putMapping(String indexName, String type, XContentBuilder mapping) {
        PutMappingRequest mappingRequest = Requests.putMappingRequest(indexName).type(type).source(mapping);
        PutMappingResponse response = getClient().admin().indices().putMapping(mappingRequest).actionGet();
        return response.isAcknowledged();
    }

.field(“fielddata”,”true”) //text字段默认不允许排序,是单独设置数据格式

20173 月23

NutzWk: 微信AccessToken没有持久化造成超出调用限制的问题解决

1、wx_config 实体类添加三个字段,对应的表结构也要手动修改:


    @Column
    @Comment("access_token")
    @ColDefine(type = ColType.VARCHAR, width = 255)
    private String access_token;

    @Column
    @Comment("access_token_expires")
    @ColDefine(type = ColType.INT)
    private Integer access_token_expires;

    @Column
    @Comment("access_token_lastat")
    @ColDefine(type = ColType.VARCHAR, width = 50)
    private String access_token_lastat;

    get  set ...方法生成出来

2、nutzwx版本升级为1.r.61-SNAPSHOT

        <dependency>
            <groupId>org.nutz</groupId>
            <artifactId>nutzwx</artifactId>
            <version>1.r.61-SNAPSHOT</version>
        </dependency>


3、nutz版本升级为1.r.60

        <dependency>
            <groupId>org.nutz</groupId>
            <artifactId>nutz</artifactId>
            <version>1.r.60</version>
        </dependency>

4、WxConfigService 类getWxApi2替换为如下代码(主要是DaoAccessTokenStore从数据库取access_token)

    public WxApi2 getWxApi2(String wxid) {
        Wx_config appInfo = this.fetch(Cnd.where("id", "=", wxid));
        DaoAccessTokenStore myDaoAccessTokenStore = new DaoAccessTokenStore(dao());
        Map<String, Object> params = new HashMap<>();
        params.put("id", appInfo.getId());
        myDaoAccessTokenStore.setTableAccessToken("access_token");
        myDaoAccessTokenStore.setTableAccessTokenExpires("access_token_expires");
        myDaoAccessTokenStore.setTableAccessTokenLastat("access_token_lastat");
        myDaoAccessTokenStore.setFetch("select access_token,access_token_expires,access_token_lastat from wx_config where id=@id");
        myDaoAccessTokenStore.setUpdate("update wx_config set access_token=@access_token, access_token_expires=@access_token_expires, access_token_lastat=@access_token_lastat where id=@id");
        myDaoAccessTokenStore.setParams(params);
        WxApi2Impl wxApi2 = new WxApi2Impl();
        wxApi2.setAppid(appInfo.getAppid());
        wxApi2.setAppsecret(appInfo.getAppsecret());
        wxApi2.setEncodingAesKey(appInfo.getEncodingAESKey());
        wxApi2.setToken(appInfo.getToken());
        wxApi2.setAccessTokenStore(myDaoAccessTokenStore);
        return wxApi2;
    }
201612 月8

Nutz:组合查询、子查询示例代码

        Cnd cnd = Cnd.NEW();
        if (!Strings.isBlank(src)) {
            cnd.and("srcFrom", "=", src);
        }
        if (!Strings.isBlank(name)) {
            String[] n = StringUtils.split(name, " ");
            SqlExpressionGroup group = new SqlExpressionGroup();
            for (String s : n) {
                SqlExpression sqlExpression = Cnd.exp("srcName", "like", "%" + s + "%");
                group.or(sqlExpression);
            }
            cnd.and(group);
        }
        cnd.and(Cnd.exps("productSku", "=", "").or("productSku", "is", null));
201610 月20

Nutz:fetchLinks 三级关联表的应用

        Shop_goods_type obj = shopGoodsTypeService.fetch(id);
        shopGoodsTypeService.fetchLinks(obj, null, Cnd.orderBy().asc("location"));
        for (Shop_goods_type_props o : obj.getPropsList()) {
            shopGoodsTypeService.dao().fetchLinks(o, null, Cnd.orderBy().asc("location"));
        }
20156 月11

Nutz:集成 CXF Webservice [通过cxf-servlet.xml配置文件]

pom.xml

	<!--CXF START-->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-api</artifactId>
            <version>2.7.15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>2.7.15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-bindings-soap</artifactId>
            <version>2.7.15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>2.7.15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-ws-security</artifactId>
            <version>2.7.15</version>
        </dependency>
        <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-web</artifactId>
           <version>4.1.5.RELEASE</version>
        </dependency>
        <!--CXF END-->

web.xml

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/webservice/*</url-pattern>
    </servlet-mapping>

cxf-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xmlns:soap="http://cxf.apache.org/bindings/soap"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">

    <!-- IP地址输入拦截器 -->
    <bean id="ipAddressInInterceptor"
          class="com.auto.webservice.servlet.IpAddressInInterceptor" />

    <jaxws:server id="workflow" serviceClass="com.auto.webservice.server.WorkflowService" address="/workflow">
        <jaxws:serviceBean>
            <bean class="com.auto.webservice.server.WorkflowServiceImpl" />
        </jaxws:serviceBean>
    </jaxws:server>
</beans>

接口类
WorkflowService.java

package com.auto.webservice.server;

import org.nutz.json.Json;

import javax.jws.WebService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wizzer on 15-4-13.
 */
@WebService(targetNamespace = "http://server.webservice.auto.com/", name = "workflowServicePortType")
public interface WorkflowService {

    @WebResult(name = "return")
    @Action(input = "urn:start", output = "urn:startResponse")
    @RequestWrapper(localName = "start")
    @WebMethod(action = "urn:start")
    @ResponseWrapper(localName = "startResponse")
    String start(@WebParam(name = "flowKey") String flowKey, @WebParam(name = "userId") String userId);//启动流程
}

实现类

WorkflowServiceImpl.java
package com.auto.webservice.server;

import org.activiti.engine.*;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.form.StartFormData;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.json.JsonFormat;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.Mvcs;

import javax.jws.WebService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wizzer on 15-4-13.
 */
@IocBean(name = "workflowService")
@WebService
public class WorkflowServiceImpl implements WorkflowService {
    private final Log log = Logs.get();
    FormService formService=Mvcs.ctx().getDefaultIoc().get(FormService.class);
    IdentityService identityService=Mvcs.ctx().getDefaultIoc().get(IdentityService.class);
    RuntimeService runtimeService=Mvcs.ctx().getDefaultIoc().get(RuntimeService.class);
    TaskService taskService=Mvcs.ctx().getDefaultIoc().get(TaskService.class);
    RepositoryService repositoryService=Mvcs.ctx().getDefaultIoc().get(RepositoryService.class);

    /**
     * 启动一个流程
     *
     * @param flowKey 流程模型key
     * @param userId  用户ID
     * @return
     */
    public String start(String flowKey, String userId) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            if (!Strings.isEmpty(userId)) {
                identityService.setAuthenticatedUserId(userId);
            }
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(flowKey);
            map.put("errcode", 0);
            map.put("errmsg", "");
            map.put("processInstanceId", processInstance.getId());
        } catch (Exception e) {
            log.error("WebServcice启动流程出错", e);
            map.put("errcode", 1);
            map.put("errmsg", e.getMessage());
        } finally {
            identityService.setAuthenticatedUserId(null);
        }
        return Json.toJson(map, JsonFormat.compact());
    }


}
 
/**
 * IP地址拦截器
 *
 * @author Sunshine
 */

public class IpAddressInInterceptor extends AbstractPhaseInterceptor {
    private final Log log = Logs.get();

    public IpAddressInInterceptor() {
        super(Phase.RECEIVE);
    }

    public void handleMessage(Message message) throws Fault {
        HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);
        //获取所有的白名单访问主机
        UserInfoServiceImpl userInfoService = Mvcs.ctx().getDefaultIoc().get(UserInfoServiceImpl.class);
        List hostList = userInfoService.queryHostListByType("w");
        // 取客户端IP地址
        String ipAddress = request.getRemoteAddr();
        // 如果允许访问的集合非空,继续处理,否则认为全部IP地址均合法
        if (!hostList.isEmpty()) {
            boolean contains = false;
            for (ListInfo hostInfo : hostList) {
                log.debug("hostInfo:" + hostInfo.getIpaddress() + "|ipAddress:" + ipAddress);
                if (hostInfo.getIpaddress().equals(ipAddress)) {
                    contains = true;
                    break;
                }
            }
            if (!contains) {
                throw new Fault(new IllegalAccessException("IP address " + ipAddress + " is not allowed"));
            }
        }
    }

}
20155 月18

Activiti:获取节点走向

 @At
    public String test5(@Param("taskId") String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null)
            return "null";
        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(task.getProcessDefinitionId());
        List<ActivityImpl> activitiList = def.getActivities();
        System.out.println("getTaskDefinitionKey:::" + task.getTaskDefinitionKey());
        NutMap map = new NutMap();
        int type = 0;
        getTaskActivitys(task.getTaskDefinitionKey(), activitiList, type, map);
        return Json.toJson(map);
    }

    public static List<PvmActivity> getTaskActivitys(String activityId, List<ActivityImpl> activityList, int type, NutMap map) {
        List<PvmActivity> activitiyIds = new ArrayList<>();
        for (ActivityImpl activityImpl : activityList) {
            String id = activityImpl.getId();

            if (activityId.equals(id)) {
                List<PvmTransition> outgoingTransitions = activityImpl.getOutgoingTransitions();//获取某节点所有线路
                List<NutMap> list = new ArrayList<>();
                for (PvmTransition tr : outgoingTransitions) {
                    NutMap map1 = new NutMap();
                    PvmActivity ac = tr.getDestination();//获取线路的终点节点
                    if (ac.getProperty("type").equals("userTask")) {
                        map.setv("type", type++);
                        map1.setv("id", ac.getId());
                        map1.setv("name", ac.getProperty("name"));
                        String conditionText=Strings.sNull(tr.getProperty("conditionText"));
                        if(!Strings.isEmpty(conditionText)){
                            map1.setv("conditionText",conditionText );
                        }
                        list.add(map1);
                    } else if (ac.getProperty("type").equals("exclusiveGateway")) {
                        getTaskActivitys(ac.getId(), activityList, type, map);
                    } else {
                        map.setv("type", type++);
                        break;
                    }
                }
                if (list.size() > 0)
                    map.addv("list", list);
                break;
            }
        }
        return activitiyIds;
    }
20155 月12

Nutz:集成CXF webservice

通过配置文件实现(这个支持CXF spring注解)点这里

pom.xml

	<!--CXF START-->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-api</artifactId>
            <version>2.7.15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>2.7.15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-bindings-soap</artifactId>
            <version>2.7.15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>2.7.15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-ws-security</artifactId>
            <version>2.7.15</version>
        </dependency>
        <!--CXF END-->

web.xml

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>com.auto.webservice.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/webservice/*</url-pattern>
    </servlet-mapping>
CXFServlet.java:
package com.auto.webservice.servlet;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.frontend.ServerFactoryBean;
import org.apache.cxf.transport.servlet.CXFNonSpringServlet;
import org.nutz.ioc.Ioc;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.Mvcs;

import javax.jws.WebService;
import javax.servlet.ServletConfig;
import javax.xml.namespace.QName;

/**
 * Created by wizzer on 15-4-10.
 */
@SuppressWarnings("serial")
public class CXFServlet extends CXFNonSpringServlet {
    private final Log log = Logs.get();

    @Override
    protected void loadBus(ServletConfig sc) {
        super.loadBus(sc);

        //全局配置
        Bus bus = getBus();
        //添加白名单过滤器
        bus.getInInterceptors().add(new IpAddressInInterceptor());
        //使用全局配置
        BusFactory.setDefaultBus(bus);
        Ioc ioc = Mvcs.ctx().getDefaultIoc();
        for (String name : ioc.getNames()) {
            try {
                Object obj = ioc.get(null, name);
                if (!obj.getClass().getPackage().getName().equals("com.auto.webservice.server")) {
                    continue;
                }
                if (obj.getClass().getAnnotation(WebService.class) == null)
                    continue;
                Class face = Class.forName(obj.getClass().getPackage().getName() + "." + Strings.upperFirst(name));
                ServerFactoryBean serverFactoryBean = new ServerFactoryBean();
                // 设置服务接口类
                serverFactoryBean.setServiceClass(face);
                // 服务请求路径
                serverFactoryBean.setAddress("/" + name.substring(0, name.indexOf("Service")));
                // 设置服务实现类
                serverFactoryBean.setServiceBean(obj);
                serverFactoryBean.setBindingId("http://schemas.xmlsoap.org/wsdl/soap12/");
                serverFactoryBean.create();
            } catch (Throwable e) {
            }
        }


    }
}

接口类

WorkflowService.java
package com.auto.webservice.server;

import org.nutz.json.Json;

import javax.jws.WebService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wizzer on 15-4-13.
 */
@WebService
public interface WorkflowService {
    String start(String flowKey, String userId);//启动流程

}

 

实现类

WorkflowServiceImpl.java

 

package com.auto.webservice.server;

import org.activiti.engine.*;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.form.StartFormData;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.json.JsonFormat;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.Mvcs;

import javax.jws.WebService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wizzer on 15-4-13.
 */
@IocBean(name = "workflowService")
@WebService
public class WorkflowServiceImpl implements WorkflowService {
    private final Log log = Logs.get();
    @Inject
    Dao dao;
    @Inject
    FormService formService;
    @Inject
    IdentityService identityService;
    @Inject
    RepositoryService repositoryService;
    @Inject
    RuntimeService runtimeService;
    @Inject
    TaskService taskService;

    /**
     * 启动一个流程
     *
     * @param flowKey 流程模型key
     * @param userId  用户ID
     * @return
     */
    public String start(String flowKey, String userId) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            if (!Strings.isEmpty(userId)) {
                identityService.setAuthenticatedUserId(userId);
            }
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(flowKey);
            map.put("errcode", 0);
            map.put("errmsg", "");
            map.put("processInstanceId", processInstance.getId());
        } catch (Exception e) {
            log.error("WebServcice启动流程出错", e);
            map.put("errcode", 1);
            map.put("errmsg", e.getMessage());
        } finally {
            identityService.setAuthenticatedUserId(null);
        }
        return Json.toJson(map, JsonFormat.compact());
    }


}

 

20155 月7

Nutz:随笔01

在nutz filter作用域内才能Mvcs.getIoc()

其他情况:

Mvcs.ctx().getDefaultIoc().get(xxx.class)
20154 月27

Nutz 集成Activiti5.17.0 [03]扩展用户、组使用自己的数据表

首先值得一提的是,taskService.createTaskQuery().taskCandidateOrAssigned(userId) 方法有bug,不会调用重写的工厂类,请使用taskService.createTaskQuery().taskCandidateUser(userId)方法。

CustomGroupEntityManager

/**
 * 分组工厂类
 * Created by wizzer on 15-4-27.
 */
@IocBean
public class CustomGroupEntityManager extends GroupEntityManager {
    Dao dao= Mvcs.getIoc().get(Dao.class);
    private final Log log = Logs.get();

    @Override
    public List<Group> findGroupsByUser(String userId) {
        Sql sql = Sqls.create("SELECT a.* FROM sys_role a,sys_user_role b WHERE a.id=b.roleid AND b.userid=@c");
        sql.params().set("c", userId);
        sql.setCallback(Sqls.callback.maps());
        dao.execute(sql);
        List<Map> list=sql.getList(Map.class);
        List<Group> groupList=new ArrayList<Group>();
        for (Map m:list){
            GroupEntity group=new GroupEntity();
            group.setId(Strings.sNull(m.get("id")));
            group.setName(Strings.sNull(m.get("name")));
            group.setType("assignment");
            group.setRevision(1);
            groupList.add(group);
        }
        return groupList;
    }
}

 

CustomGroupEntityManagerFactory

/**
 * 分组接口类
 * Created by wizzer on 15-4-27.
 */
@IocBean
public class CustomGroupEntityManagerFactory implements SessionFactory {
    private GroupEntityManager groupEntityManager;
    public void setGroupEntityManager(GroupEntityManager groupEntityManager) {
        this.groupEntityManager = groupEntityManager;
    }
    @Override
    public Class<?> getSessionType() {
        return GroupIdentityManager.class;
    }
    @Override
    public Session openSession() {
        return groupEntityManager;
    }
}

 

CustomUserEntityManager

/**
 * 用户工厂类
 * Created by wizzer on 15-4-24.
 */
@IocBean
public class CustomUserEntityManager extends UserEntityManager {
    Dao dao= Mvcs.getIoc().get(Dao.class);
    private final Log log = Logs.get();

    @Override
    public User findUserById(String userId) {

        log.info("findUserById:::::::::::::::::::::::::::::::"+userId);
        UserEntity userEntity = new UserEntity();
        Sys_user sysUser = dao.fetch(Sys_user.class, Cnd.where("uid", "=", userId));
        userEntity.setId(userId);
        userEntity.setFirstName(sysUser.getRealname());
        userEntity.setEmail(sysUser.getEmail());
        userEntity.setRevision(1);
        return userEntity;
    }

    @Override
    public List<Group> findGroupsByUser(String userId) {
        // TODO Auto-generated method stub
        log.info("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
        Sql sql = Sqls.create("SELECT a.* FROM sys_role a,sys_user_role b WHERE a.id=b.roleid AND b.userid=@c");
        sql.params().set("c", userId);
        sql.setCallback(Sqls.callback.maps());
        dao.execute(sql);
        List<Map> list=sql.getList(Map.class);
        List<Group> groupList=new ArrayList<Group>();
        for (Map m:list){
            GroupEntity group=new GroupEntity();
            group.setId(Strings.sNull(m.get("id")));
            group.setName(Strings.sNull(m.get("name")));
            group.setType("assignment");
            group.setRevision(1);
            groupList.add(group);
        }
        return groupList;
    }


}

 

CustomUserEntityManagerFactory

/**
 * 用户接口类
 * Created by wizzer on 15-4-24.
 */
@IocBean
public class CustomUserEntityManagerFactory implements SessionFactory {
    private UserEntityManager userEntityManager;
    public void setUserEntityManager(UserEntityManager userEntityManager) {
        this.userEntityManager = userEntityManager;
    }
    @Override
    public Class<?> getSessionType() {
        return UserIdentityManager.class;
    }
    @Override
    public Session openSession() {
        return userEntityManager;
    }
}

Nutz 集成Activiti5.17.0 [01]初始化activiti

在初始化activiti时追加代码:

        List<SessionFactory> list=new ArrayList<SessionFactory>();
        CustomGroupEntityManagerFactory customGroupManagerFactory=new CustomGroupEntityManagerFactory();
        customGroupManagerFactory.setGroupEntityManager(new CustomGroupEntityManager());
        CustomUserEntityManagerFactory customUserEntityManagerFactory=new CustomUserEntityManagerFactory();
        customUserEntityManagerFactory.setUserEntityManager(new CustomUserEntityManager());
        list.add(customGroupManagerFactory);
        list.add(customUserEntityManagerFactory);
        processEngineConfiguration.setCustomSessionFactories(list);
20154 月27

Nutz 集成Activiti5.17.0 [02]集成流程设计器及汉化

下载 activiti-modeler 源码,里面的方法用nutz重写,然后修改editor-app里面页面和js里对应的路径。

5.17.0汉化文件下载:http://pan.baidu.com/s/1qWlzHDE

细节不多述了,自己动手吧,哇哈哈……

20154 月27

Nutz 集成Activiti5.17.0 [01]初始化activiti

@SetupBy(value=StartSetup.class)
public class MainModule {
}

private void activitiInit(NutConfig config) {
        log.info("Activiti Init Start...");
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) ProcessEngineConfiguration
                .createStandaloneProcessEngineConfiguration();

        processEngineConfiguration.setDataSource(config.getIoc().get(DataSource.class));
        processEngineConfiguration.setDatabaseSchemaUpdate("false");
        processEngineConfiguration.setJobExecutorActivate(false);
        processEngineConfiguration.setActivityFontName("宋体");
        processEngineConfiguration.setLabelFontName("宋体");
        processEngineConfiguration.setXmlEncoding("utf-8");
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
        ((Ioc2) config.getIoc()).getIocContext().save("app", "processEngine", new ObjectProxy(processEngine));
        ((Ioc2) config.getIoc()).getIocContext().save("app", "repositoryService", new ObjectProxy(processEngine.getProcessEngineConfiguration().getRepositoryService()));
        ((Ioc2) config.getIoc()).getIocContext().save("app", "runtimeService", new ObjectProxy(processEngine.getProcessEngineConfiguration().getRuntimeService()));
        ((Ioc2) config.getIoc()).getIocContext().save("app", "taskService", new ObjectProxy(processEngine.getProcessEngineConfiguration().getTaskService()));
        ((Ioc2) config.getIoc()).getIocContext().save("app", "formService", new ObjectProxy(processEngine.getProcessEngineConfiguration().getFormService()));
        ((Ioc2) config.getIoc()).getIocContext().save("app", "historyService", new ObjectProxy(processEngine.getProcessEngineConfiguration().getHistoryService()));
        ((Ioc2) config.getIoc()).getIocContext().save("app", "managementService", new ObjectProxy(processEngine.getProcessEngineConfiguration().getManagementService()));
        ((Ioc2) config.getIoc()).getIocContext().save("app", "identityService", new ObjectProxy(processEngine.getProcessEngineConfiguration().getIdentityService()));
        log.info("Activiti Init End.");

    }
20154 月24

Velocity:文本模板渲染

public String getTemplateStr(String template, Map<String, String> para) {
        StringWriter writer = new StringWriter();
        try {
            RuntimeServices runtimeServices = RuntimeSingleton.getRuntimeServices();
            StringReader reader = new StringReader(template);
            SimpleNode node = runtimeServices.parse(reader, "Template name");
            Template t = new Template();
            t.setRuntimeServices(runtimeServices);
            t.setData(node);
            t.initDocument();
            VelocityContext context = new VelocityContext();
            if (para.size() > 0) {
                for (String key : para.keySet()) {
                    context.put(key, para.get(key));
                }
            }
            t.merge(context, writer);
        } catch (Exception e) {
            throw new RuntimeException("Error commiting transaction! cause:"+ e.getMessage());
        }
        return writer.toString();
    }
    
    @At("/form")
    @Ok("vm:template.private.test")
    public void form(HttpServletRequest req, HttpServletResponse resp) {
        Map<String, Object> formParams = new HashMap<String, Object>();
        formParams.put("formKey", "form/waizhibiaodan/01/01.form");
        int timeout = 60 * 1000;
        String str = Http.post("http://127.0.0.1/test/getFormKey", formParams, timeout);
        NutMap map = Json.fromJson(NutMap.class, str);
        String formData = map.getString("data");
        Map<String, String> params = new HashMap<String, String>();
        params.put("startDate", "2015-04-21");
        params.put("endDate", "2015-04-25");
        req.setAttribute("formData", getTemplateStr(formData, params));
    }
20154 月21

Nutz:打包命令

mvn package -Dmaven.test.skip=true

20151 月28

Nutz:通过Java代码生成表格图片

效果图:

i

源码不解释:

 private void ResultToJpg(List<Map> list, String title, HttpServletResponse response) {
        BufferedImage image;
        int totalrow = list.size();
        int totalcol = 7;
        int imageWidth = 640;
        int rowheight = 40;
        int imageHeight = totalrow * rowheight + 105;
        int startHeight = 20;
        int startWidth = 10;
        int colwidth = (int) ((imageWidth - 20) / totalcol);
        int colwidth2 = (int) ((imageWidth - 20) / (totalcol + 1));
        image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = (Graphics2D) image.getGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, imageWidth, imageHeight);
        Font font = new Font("微软雅黑", Font.BOLD, 20);
        graphics.setFont(font);
        graphics.setColor(new Color(112, 48, 158));
        graphics.drawString(Strings.sNull(title), startWidth, startHeight + 10);
        font = new Font("微软雅黑", Font.PLAIN, 13);
        graphics.setColor(new Color(99, 99, 99));
        graphics.setFont(font);
        String subtitle1 = "欢迎访问";
        graphics.drawString(subtitle1, startWidth, startHeight + 30);
        font = new Font("Arial, Helvetica, sans-serif", Font.BOLD, 13);
        graphics.setColor(new Color(112, 48, 158));
        graphics.setFont(font);
        String subtitle2 = "inm.xuetang.cn";
        graphics.drawString(subtitle2, startWidth + 60, startHeight + 30);
        font = new Font("微软雅黑", Font.PLAIN, 13);
        graphics.setColor(new Color(99, 99, 99));
        graphics.setFont(font);
        String subtitle3 = "冲榜、制榜!";
        graphics.drawString(subtitle3, startWidth + 165, startHeight + 30);
        //画首行
        graphics.setColor(new Color(112, 48, 158));
        graphics.fillRect(startWidth, startHeight + rowheight, imageWidth - 20, rowheight);
        font = new Font("微软雅黑", Font.PLAIN, 12);
        graphics.setColor(Color.WHITE);
        graphics.setFont(font);
        graphics.drawString("#", startWidth + 10, startHeight + rowheight * 2 - 15);
        graphics.drawString("公众号", startWidth + colwidth - 10, startHeight + rowheight * 2 - 15);
        //画表头
        String[] headCells = {"发布", "总阅读数", "  头条", " 平均 ", "总点赞数", " WCI"};
        for (int m = 0; m < headCells.length; m++) {
            graphics.drawString(headCells[m].toString(), startWidth + colwidth2 * m + colwidth * 2, startHeight + rowheight * 2 - 15);
        }
        //画行
        for (int j = 0; j < totalrow; j++) {
            if (j % 2 == 0) {
                graphics.setColor(new Color(255, 255, 255));
            } else {
                graphics.setColor(new Color(238, 238, 238));
            }
            graphics.fillRect(startWidth, startHeight + (j + 1) * rowheight + 40, imageWidth - 20, startHeight + (j + 1) * rowheight);
            font = new Font("微软雅黑", Font.BOLD, 12);
            graphics.setColor(new Color(112, 48, 158));
            graphics.setFont(font);
            graphics.drawString(String.valueOf(j + 1), startWidth + 10, startHeight + (j + 3) * rowheight - 15);
            Map map = list.get(j);
            font = new Font("微软雅黑", Font.PLAIN, 12);
            graphics.setColor(new Color(0, 0, 0));
            graphics.setFont(font);
            graphics.drawString(Strings.sNull(map.get("wx_nickname")), startWidth + (colwidth / 2) - 10, startHeight + (j + 3) * rowheight - 25);
            graphics.drawString(Strings.sNull(map.get("wx_name")), startWidth + (colwidth / 2) - 10, startHeight + (j + 3) * rowheight - 10);
            graphics.drawString(Strings.sNull(map.get("url_times")) + "/" + Strings.sNull(map.get("url_num")), startWidth + colwidth2 + colwidth + 10, startHeight + (j + 3) * rowheight - 15);
            if (NumberUtils.toInt(Strings.sNull(map.get("url_num_10w"))) > 0) {
                String readnum_all = Strings.sNull(map.get("readnum_all"));
                int t = NumberUtils.toInt(readnum_all.substring(readnum_all.length() - 4, readnum_all.length() - 3));
                if (t > 0) {
                    readnum_all = readnum_all.substring(0, readnum_all.length() - 4) + "." + String.valueOf(t);
                } else {
                    readnum_all = readnum_all.substring(0, readnum_all.length() - 4);
                }
                graphics.drawString(readnum_all + "万+", startWidth + colwidth2 + colwidth * 2 + 5, startHeight + (j + 3) * rowheight - 15);
            } else {
                graphics.drawString(Strings.sNull(map.get("readnum_all")), startWidth + colwidth2 + colwidth * 2 + 5, startHeight + (j + 3) * rowheight - 15);
            }
            if (NumberUtils.toInt(Strings.sNull(map.get("url_times_readnum"))) > 100000) {
                String url_times_readnum = Strings.sNull(map.get("url_times_readnum"));
                int t = NumberUtils.toInt(url_times_readnum.substring(url_times_readnum.length() - 4, url_times_readnum.length() - 3));
                if (t > 0) {
                    url_times_readnum = url_times_readnum.substring(0, url_times_readnum.length() - 4) + "." + String.valueOf(t);
                } else {
                    url_times_readnum = url_times_readnum.substring(0, url_times_readnum.length() - 4);
                }
                graphics.drawString(url_times_readnum+"万+", startWidth + colwidth2 + colwidth * 3 - 5, startHeight + (j + 3) * rowheight - 15);
            } else {
                graphics.drawString(Strings.sNull(map.get("url_times_readnum")), startWidth + colwidth2 + colwidth * 3 - 5, startHeight + (j + 3) * rowheight - 15);
            }
            if (NumberUtils.toInt(Strings.sNull(map.get("readnum_av"))) > 100000) {
                String readnum_av = Strings.sNull(map.get("readnum_av"));
                int t = NumberUtils.toInt(readnum_av.substring(readnum_av.length() - 4, readnum_av.length() - 3));
                if (t > 0) {
                    readnum_av = readnum_av.substring(0, readnum_av.length() - 4) + "." + String.valueOf(t);
                } else {
                    readnum_av = readnum_av.substring(0, readnum_av.length() - 4);
                }
                graphics.drawString(readnum_av+"万+", startWidth + colwidth2 + colwidth * 4 - 15, startHeight + (j + 3) * rowheight - 15);
            } else {
                graphics.drawString(Strings.sNull(map.get("readnum_av")), startWidth + colwidth2 + colwidth * 4 - 15, startHeight + (j + 3) * rowheight - 15);

            }
            graphics.drawString(Strings.sNull(map.get("likenum_all")), startWidth + colwidth2 + colwidth * 5 - 20, startHeight + (j + 3) * rowheight - 15);
            graphics.drawString(Strings.sNull(map.get("wci")), startWidth + colwidth2 + colwidth * 6 - 40, startHeight + (j + 3) * rowheight - 15);
        }
        //末行
        graphics.setColor(Color.WHITE);
        graphics.fillRect(startWidth, startHeight + (totalrow + 1) * rowheight + 40, imageWidth - 20, startHeight + (totalrow + 1) * rowheight);
        try {
            response.setContentType("image/png");
            OutputStream out = response.getOutputStream();
            ImageIO.write(image, "png", out);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }