paulwong

#

csv 文件打开乱码,有哪些方法可以解决?

Excel 在读取 csv 的时候是通过读取文件头上的 bom 来识别编码的,这导致如果我们生成 csv 文件的平台输出无 bom 头编码的 csv 文件(例如 utf-8 ,在标准中默认是可以没有 bom 头的),Excel 只能自动按照默认编码读取,不一致就会出现乱码问题了。

掌握了这点相信乱码已经无法阻挡我们前进的步伐了:只需将不带 bom 头编码的 csv 文件,用文本编辑器(工具随意,推荐 notepad++ )打开并转换为带 bom 的编码形式(具体编码方式随意),问题解决。

当然,如果你是像我一样的码农哥哥,在生成 csv 文件的时候写入 bom 头更直接点,用户会感谢你的。

附录:对于 utf-8 编码,unicode 标准中是没有 bom 定义的,微软在自己的 utf-8 格式的文本文件之前加上了EF BB BF三个字节作为识别此编码的 bom 头,这也解释了为啥大部分乱码都是 utf-8 编码导致的原因

SPRING BATCH中生成CSV文件时的解决方案:
new FlatFileItemWriterBuilder<T>()
      .name(itemWriterName)
      .resource(outputResource)
      .lineAggregator(lineAggregator)
      .headerCallback(
      h -> {
               System.out.println(header);
               h.write('\uFEFF');//只需加这一行
               h.write(header);
           }
      )
      .build();

https://stackoverflow.com/questions/48952319/send-csv-file-encoded-in-utf-8-with-bom-in-java

posted @ 2021-03-23 10:30 paulwong 阅读(388) | 评论 (0)编辑 收藏

java泛型 方法返回值带有泛型


        /**
         * 
         * 
@param <T>声明此方法持有一个类型T,也可以理解为声明此方法为泛型方法
         * 
@param clazz 作用是指明泛型T的具体类型
         * 
@return 指明该方法的返回值为类型T
         * 
@throws InstantiationException
         * 
@throws IllegalAccessException
         
*/
        public <T> T getObject(Class<T> clazz) throws InstantiationException, IllegalAccessException {
            T t = clazz.newInstance();//创建对象
            return t;
        }

方法返回值前的<T>的左右是告诉编译器,当前的方法的值传入类型可以和类初始化的泛型类不同,也就是该方法的泛型类可以自定义,不需要跟类初始化的泛型类相同

posted @ 2021-03-19 15:10 paulwong 阅读(661) | 评论 (0)编辑 收藏

领域驱动设计实战

领域驱动(DDD,Domain Driven Design)为软件设计提供了一套完整的理论指导和落地实践,通过战略设计和战术设计,将技术实现与业务逻辑分离,来应对复杂的软件系统。本系列文章准备以实战的角度来介绍 DDD,首先编写领域驱动的代码模型,然后再基于代码模型,引入 DDD 的各项概念,先介绍战术设计,再介绍战略设计。

DDD 实战1 - 基础代码模型
DDD 实战2 - 集成限界上下文(Rest & Dubbo)
> DDD 实战3 - 集成限界上下文(消息模式)
> DDD 实战4 - 领域事件的设计与使用
> DDD 实战5 - 实体与值对象
> DDD 实战6 - 聚合的设计
> DDD 实战7 - 领域工厂与领域资源库
> DDD 实战8 - 领域服务与应用服务
> DDD 实战9 - 架构设计
> DDD 实战10 - 战略设计

posted @ 2021-02-25 15:53 paulwong 阅读(278) | 评论 (0)编辑 收藏

JSR-303 Bean Validation - Date String Validation

@Past @Future只针对Date类型的验证,如果是String类型的验证,则不适用。
其实可以新加一个方法返回Date类型,再配合@Future@Past 进行验证。

@Future(message = "Invalid CN_ID_INFO.EXPIRE_DATE.")
private LocalDate getValidExpireDate() {
    try {
        return LocalDate.parse(this.dateString, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    } catch (Exception e) {
        return null;
    }
}

此方法对dateString进行解释,返回LocalDate,如果dateString为空或格式错误,则返回空,再配合@Future 进行是否未来日期的验证。

posted @ 2021-02-25 09:44 paulwong 阅读(336) | 评论 (0)编辑 收藏

JSR-303 Bean Validation - Conditional Validation

bean validation的注释是针对单个变量的,如果要针对多个变量的联动,则不行,需要用到这个注释。
这种方法避免了自定义校验器而增加类。
https://www.chkui.com/article/java/java_bean_validation

@AssertTrue(message = "Missing BANK_CARD_IMG_INFO.IMG")
private Boolean getValidImg() {
    if(YNEnum.Y.code.equals(super.getNeedChecked())) {
            return StringUtils.hasText(this.img);
        }
        return null;//igore checking.
}

这个是当needChecked为Y的时候才执行检查img变量是否为空。
有几点注意:
  1. 方法名称要以get开头
  2. 返回类型用Boolean,而不用boolean
  3. 返回值有三种:true,false,null如果是null则当成通过,与true的结果一样

posted @ 2021-02-25 09:24 paulwong 阅读(328) | 评论 (0)编辑 收藏

http与https的区别

http是将内容文字通过tcp传送,内容是明文,未经加密,可透过tcpdump偷看。

https将内容文字用不对称的方式加密后再传送,加密协议是TLS或SSL,透过tcpdump看到的内容是乱码。而且服务器端更换密钥无需通知client。

How hackers love your HTTP data
https://blog.usejournal.com/how-hackers-love-your-http-data-157e76f2c66a

How you could get hacked at a coffee shop
https://medium.com/bugbountywriteup/how-you-could-get-hacked-at-a-coffee-shop-3a81a53c0b4f


Hacker Lexicon: What Is HTTPS?
https://www.wired.com/2016/04/hacker-lexicon-what-is-https-encryption/

posted @ 2021-02-04 17:00 paulwong 阅读(223) | 评论 (0)编辑 收藏

SPRING BOOT单元测试之@ActiveProfiles

SPRING BOOT单元测试中,因为测试时可能对应的服务器地址不同于SIT等别的环境,通常会将这些地址放于application-sit.yaml中。
在单元测试的代码中用这个标签指定用哪个profile,如
@ActiveProfiles({"embedded-mongodb","test"})

但这样做法,由于@ActiveProfiles这个标签是final的,如果要测试别的profile,只能复制另一份单元测试代码,再改此标签。

比较灵活的做法是用default profile,default profile是如果没指定任何profile,则会默认用这个。在application-default.yaml中再指定需激活的profile。
spring:
   profiles:
      active: test,embedded-mongodb

如果要测试别的profile,可以指定环境变量的方式覆盖:
-Dspring.profiles.active=error,embedded-mongodb

为了安全起见,将application-default.yaml放在测试目录中:src\test\resources。

Setting default Spring profile for tests with override option
https://blog.inspeerity.com/spring/setting-default-spring-profile-for-tests-with-override-option/




posted @ 2021-02-04 15:31 paulwong 阅读(1844) | 评论 (0)编辑 收藏

JSR-303 Bean Validation

接收数据的JAVA BEAN通常需要验证其中字段的正确性,如不准为空,符合EMAIL格式等。
JSR-303 Bean Validation则提供了这样的便捷。

只要在JAVA BEAN中需要验证的字段加@NotNull这种标签,然后在SERVISE中的输入参数中加@Valid标签,则就激活验证流程。
也可以编程的方式自己验证:
@MessageEndpoint
//@Validated
public class MqMessageCcdValidator {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(MqMessageCcdValidator.class);
    
    @Autowired
    private Validator validator;
    
    @ServiceActivator
    public MqMessage<CcdRequest> validate(/* @Valid */ Message<MqMessage<CcdRequest>> requestMessage) {
        Set<ConstraintViolation<MqMessage<CcdRequest>>> set = validator.validate(requestMessage.getPayload());
        if(CollectionUtils.isNotEmpty(set)) {
            CompositeException compositeException = new CompositeException();
            set.forEach(
                constraintViolation -> {
                                            LOGGER.info("{}", constraintViolation);
                                            ReqInfoValidationException exception =
                                                    new ReqInfoValidationException(constraintViolation.getMessage());
                                            compositeException.addException(exception);
                                       }
            );
            throw new MessageHandlingException(requestMessage, compositeException);
        }
        
        return requestMessage.getPayload();
    }

}

自定义验证规则
可用标签来做,以下为验证手机号的规则:
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Pattern;

@Retention(RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = {})
@ReportAsSingleViolation
@Pattern(regexp = "^1[3-9]\\d{9}$")
public @interface ChinaPhone {
    String message() default "Invalid Chinese mobile No.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

如果比较复杂的验证规则,则参见:
https://reflectoring.io/bean-validation-with-spring-boot/#implementing-a-custom-validator

How to use Java Bean Validation in Spring Boot
https://nullbeans.com/how-to-use-java-bean-validation-in-spring-boot/

Complete Guide to Validation With Spring Boot
https://reflectoring.io/bean-validation-with-spring-boot/

Spring JMS Validate Messages using JSR-303 Bean Validation
https://memorynotfound.com/spring-jms-validate-messages-jsr-303-bean-validation/

Spring REST Validation Example
https://mkyong.com/spring-boot/spring-rest-validation-example/

Spring Boot 整合 Bean Validation 校验数据

https://blog.csdn.net/wangzhihao1994/article/details/108403732

posted @ 2021-01-28 10:35 paulwong 阅读(346) | 评论 (0)编辑 收藏

SPRING INTEGRATION SCATTERGATHER

场景,餐厅:
  1. 食客下单,有饮品、食物、甜点
  2. 侍应接单,传送给厨房
  3. 厨房分三个子流程处理,即饮品、食物、甜点子流程
  4. 等待三个子流程处理完,合并成一份交付
  5. 如果厨房发现某食物欠缺,会通知侍应,展开错误处理,即通知食客更改食物,再交给厨房
  6. 侍应将交付品传送给食客
有一个主流程、三个子流程和一个聚合流程,聚合流程会聚合三个子流程的产物,通知主流程,再往下走。
并且主流程会感知子流程的错误,并会交给相应错误处理流程处理,且将结果再交给聚合流程。

对应SPRING INTEGRATION 的SCATTERGATHER模式:
@Bean
public IntegrationFlow scatterGatherAndExecutorChannelSubFlow(TaskExecutor taskExecutor) {
    return f -> f
            .scatterGather(
                    scatterer -> scatterer
                            .applySequence(true)
                            .recipientFlow(f1 -> f1.transform(p -> "Sub-flow#1"))
                            .recipientFlow(f2 -> f2
                                    .channel(c -> c.executor(taskExecutor))
                                    .transform(p -> {
                                        throw new RuntimeException("Sub-flow#2");
                                    })),
                    null,
                    s -> s.errorChannel("scatterGatherErrorChannel"));
}

@ServiceActivator(inputChannel = "scatterGatherErrorChannel")
public Message<?> processAsyncScatterError(MessagingException payload) {
    return MessageBuilder.withPayload(payload.getCause().getCause())
            .copyHeaders(payload.getFailedMessage().getHeaders())
            .build();
}

https://github.com/adnanmamajiwala/spring-integration-sample/tree/master/dsl-scatter-gather/src/main/java/com/si/dsl/scattergather

https://docs.spring.io/spring-integration/docs/5.1.x/reference/html/#scatter-gather

posted @ 2021-01-28 10:11 paulwong 阅读(313) | 评论 (0)编辑 收藏

「测试」 - 接口测试 & mock工具Moco

当需要调用第三方HTTP接口时,别人的接口还没完成,可先根据接口定义文档,返回适当的数据,以便开发。

在LINUX上的部署结构为:
├── boot
│   ├── moco-runner-1.1.0-standalone.jar
│   └── .version
├── foo.json
├── logs
│   ├── back
│   └── moco.log
├── moco.sh
└── startup-moco.sh

.version文件:
/path/to/boot/moco-runner-1.1.0-standalone.jar 1.1.0

moco.sh
#!/usr/bin/env bash

# Ensure this file is executable via `chmod a+x moco`, then place it
# somewhere on your $PATH, like ~/bin. The rest of moco will be
# installed upon first run into the ~/.moco directory.

if [ `id -u` -eq 0 ] && [ "$MOCO_ROOT" = "" ]; then
    echo "WARNING: You're currently running as root; probably by accident."
    echo "Press control-C to abort or Enter to continue as root."
    echo "Set MOCO_ROOT to disable this warning."
    read _
fi

echo $*

#export MOCO_HOME="$
{MOCO_HOME:-"$HOME/.moco"}"
export MOCO_HOME=$(cd `dirname $0`; cd boot; pwd)
VERSION_LOG_FILE="$MOCO_HOME/.version"

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

if [ "$HTTP_CLIENT" = "" ]; then
    if type -p curl >/dev/null 2>&1; then
        if [ "$https_proxy" != "" ]; then
            CURL_PROXY="-x $https_proxy"
        fi
        HTTP_CLIENT="curl $CURL_PROXY -f -L -o"
    else
        HTTP_CLIENT="wget -O"
    fi
fi

function download_failed_message 
{
    echo "Failed to download $1"
    echo "It's possible your HTTP client's certificate store does not have the"
    echo "correct certificate authority needed. This is often caused by an"
    echo "out-of-date version of libssl. Either upgrade it or set HTTP_CLIENT"
    echo "to turn off certificate checks
:"
    echo "  export HTTP_CLIENT=\"wget --no-check-certificate -O\" # or"
    echo "  export HTTP_CLIENT=\"curl --insecure -f -L -o\""
    echo "It's also possible that you're behind a firewall haven't yet"
    echo "set HTTP_PROXY and HTTPS_PROXY."
}

function download 
{
    $HTTP_CLIENT "$2.pending" "$1"
    if [ $? == 0 ]; then
        # TODO
: checksum
        mv -f "$2.pending" "$2"
    else
        rm "$2.pending" 2> /dev/null
        download_failed_message "$1"
        exit 1
    fi
}

function parse_tag 
{
   tag_value=`grep "<$2>.*<.$2>" $1 | sed -e "s/^.*<$2/<$2/" | cut -f2 -d">"| cut -f1 -d"<"`
}

function parse_maven_metadata 
{
    MOCO_METADATA_URL="http
://repo1.maven.org/maven2/com/github/dreamhead/moco-runner/maven-metadata.xml"
    MOCO_METADATA="/tmp/maven-metadata.xml"
    download $MOCO_METADATA_URL $MOCO_METADATA
    parse_tag $MOCO_METADATA latest
    LATEST_VERSION=$tag_value
}

function parse_standalone_latest_url 
{
    parse_maven_metadata
    VERSION=${LATEST_VERSION%
}
    LATEST_MOCO_STANDALONE_JAR="moco-runner-$VERSION-standalone.jar"
    MOCO_STANDLONE_URL="http://repo1.maven.org/maven2/com/github/dreamhead/moco-runner/$LATEST_VERSION/$LATEST_MOCO_STANDALONE_JAR"
}

function install 
{
    echo "Install moco"
    echo "Parse the latest version of moco"
    parse_standalone_latest_url
    echo "Download the latest moco
: $LATEST_VERSION"
    MOCO_STANDALONE="$MOCO_HOME/$LATEST_MOCO_STANDALONE_JAR"
    echo "$MOCO_STANDALONE $LATEST_VERSION" >> $VERSION_LOG_FILE
    download $MOCO_STANDLONE_URL $MOCO_STANDALONE
}

function load_current_version 
{
    read MOCO_STANDALONE CURRENT_VERSION < $VERSION_LOG_FILE
    if [[ "$(uname)" -ne "Darwin" && "$(expr substr $(uname -s) 2 6)" == "CYGWIN"   ]];then
        MOCO_STANDALONE=`cygpath -m "$MOCO_STANDALONE"`
    fi
}

function usage 
{
  printf "
options
:
       help      show help
       start     start server, e.g. moco start -p 12306 -c configfile.json
       shutdown  shutdown moco server 
       upgrade   upgrade moco
"
}

if [ ! -e "$MOCO_HOME" ]
then
    mkdir "$MOCO_HOME"
    install
fi

if [ "$1" = "start" ]; then
    echo "Starting"
    load_current_version
    exec "$JAVACMD" -jar "$MOCO_STANDALONE" $*
elif [ "$1" = "http" ]; then
    echo "Starting HTTP server"
    load_current_version
    exec "$JAVACMD" -jar "$MOCO_STANDALONE" $*
elif [ "$1" = "https" ]; then
    echo "Starting HTTPS server"
    load_current_version
    exec "$JAVACMD" -jar "$MOCO_STANDALONE" $*
elif [ "$1" = "socket" ]; then
    echo "Starting Socket server"
    load_current_version
    exec "$JAVACMD" -jar "$MOCO_STANDALONE" $*
elif [ "$1" = "shutdown" ]; then
    echo "Shutting down server"
    load_current_version
    exec "$JAVACMD" -jar "$MOCO_STANDALONE" $*
elif [ "$1" = "upgrade" ]; then
    echo "Check the new version"
    parse_maven_metadata
    load_current_version

    if [ "$LATEST_VERSION" = "$CURRENT_VERSION" ]; then
        echo "The current version of moco is the latest"
    else
        echo "Upgrading"
        rm $VERSION_LOG_FILE
        install
    fi
elif [ "$1" = "version" ]; then
    load_current_version
    echo "Moco version: $CURRENT_VERSION"
elif [ "$1" = "help" ]; then
    usage
else
    usage
fi

这是根据GIT上的原始文件作的修改。

startup-moco.sh
CMD_PATH=$(cd `dirname $0`; pwd)

# 项目日志输出绝对路径
LOG_DIR=$
{CMD_PATH}"/logs"
LOG_FILE="moco.log"
LOG_PATH="$
{LOG_DIR}/${LOG_FILE}"

# 当前时间
NOW=`date +'%Y-%m-%m-%H-%M-%S'`
NOW_PRETTY=`date +'%Y-%m-%m %H:%M:%S'`

# 启动日志
STARTUP_LOG="================================================ $
{NOW_PRETTY} ================================================\n"

# 日志备份目录
LOG_BACK_DIR="$
{LOG_DIR}/back/"

# 如果logs文件夹不存在,则创建文件夹
if [[ ! -d "$
{LOG_DIR}" ]]; then
  mkdir "$
{LOG_DIR}"
fi

# 如果logs/back文件夹不存在,则创建文件夹
if [[ ! -d "$
{LOG_BACK_DIR}" ]]; then
  mkdir "$
{LOG_BACK_DIR}"
fi

# 如果项目运行日志存在,则重命名备份
if [[ -f "$
{LOG_PATH}" ]]; then
    mv $
{LOG_PATH} "${LOG_BACK_DIR}/${APPLICATION}_back_${NOW}.log"
fi

# 创建新的项目运行日志
echo "" > $
{LOG_PATH}

# 可支持多个json配置文件
$CMD_PATH/moco.sh http -p 8088 -g "${CMD_PATH}/root.json" > ${LOG_PATH} 2>&1 &

# 打印启动日志
echo -e $
{STARTUP_LOG}

root.json
[
    
{
        "context"
: "/service-a",
        "include": "foo.json"
    
},
    
{
        "context"
: "/service-b",
        "include": "bar.json"
    
}
]

foo.json
[
    
{
        "request"
: {
            "method": "post",
            "forms": {
                "method": "uploadKycInfo"
            
}
        },
        "response": 
{
            "json"
: {
                "response": {
                    "subcode": "10000",
                    "submsg": "Success",
                    "sndDt": "20210121101800",
                    "remark": "上传验证基本信息",
                    "msgBody": {
                        "merOrdrNo": "A00120210121654321",
                        "retCode": "00000",
                        "retMsg": "成功/处理完成",
                        "remark": "上传详情或上传信息的简要描述"
                    
}
                },
                "code": "0000",
                "msg": "处理完成",
                "sign": "DF2659FE3EB8184561135D9F55F5EF5"
            }
        }
    }
]

访问路径:
http://ip:port/service-a/

https://github.com/dreamhead/moco/blob/master/moco-doc/apis.md
https://zhuanlan.zhihu.com/p/60076337 
https://www.programmersought.com/article/68272293688/

posted @ 2021-01-21 14:09 paulwong 阅读(613) | 评论 (0)编辑 收藏

仅列出标题
共116页: First 上一页 11 12 13 14 15 16 17 18 19 下一页 Last