MySQL 授权IP访问语句
GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.10.10.10' identified by 'pass123';
FLUSH PRIVILEGES;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.10.10.10' identified by 'pass123';
FLUSH PRIVILEGES;
CREATE TABLESPACE DEMO LOGGING DATAFILE '/opt/oracle/oradata/ORCLCDB/DEMO.dbf' SIZE 200M AUTOEXTEND ON NEXT 32M MAXSIZE 500M EXTENT MANAGEMENT LOCAL;
create temporary tablespace DEMO_TEMP tempfile '/opt/oracle/oradata/ORCLCDB/DEMO_TEMP.dbf' size 100m autoextend on next 32m maxsize 500m extent management local;
alter session set "_ORACLE_SCRIPT"=true;
create user DEMO identified by DEMO default tablespace DEMO temporary tablespace DEMO_TEMP;
alter user DEMO identified by DEMO;
grant connect,resource,dba to DEMO;
alter user DEMO quota unlimited on users;
nvm 切换node版本
nvm alias default 14
nvm use 14
nodes-sass 安装
npm install --target_arch=x64
location /api/v1 {
set $is_matched 0;
if ($request_uri ~ /api/v1/user/100001/ ) {
proxy_pass https://127.0.0.1:1001/;
set $is_matched 1;
}
if ($request_uri ~ /api/v1/user/100002/ ) {
proxy_pass https://127.0.0.1:1001/;
set $is_matched 1;
}
# 没有匹配到,跳转到默认页面
if ($is_matched = 0) {
proxy_pass https://127.0.0.1:8080;
}
}
采用最新Java驱动
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.3.0</version>
</dependency>
获取数据源及数据库
/**
* @author wizzer@qq.com
*/
public class ZMongoDatabase {
private MongoDatabase db;
public ZMongoDatabase(MongoDatabase db) {
this.db = db;
}
/**
* 获取集合,集合不存在则返回 null
*
* @param name 集合名称
* @return 集合薄封装
*/
public MongoCollection<Document> getCollection(String name) {
if (!this.collectionExists(name)) {
return null;
}
return db.getCollection(name);
}
/**
* 获取一个集合,如果集合不存在,就创建它
*
* @param name 集合名
* @param dropIfExists true 如果存在就清除
* @return 集合薄封装
*/
public MongoCollection<Document> createCollection(String name, boolean dropIfExists) {
// 不存在则创建
if (!this.collectionExists(name)) {
return createCollection(name, null);
}
// 固定清除
else if (dropIfExists) {
db.getCollection(name).drop();
return createCollection(name, null);
}
// 已经存在
return db.getCollection(name);
}
/**
* 获取一个集合,如果集合不存在,就创建它
*
* @param name 集合名
* @param options 集合配置信息
* @param dropIfExists true 如果存在就清除
* @return 集合薄封装
*/
public MongoCollection<Document> createCollection(String name, CreateCollectionOptions options, boolean dropIfExists) {
// 不存在则创建
if (!this.collectionExists(name)) {
return createCollection(name, options);
}
// 固定清除
else if (dropIfExists) {
db.getCollection(name).drop();
return createCollection(name, options);
}
// 已经存在
return db.getCollection(name);
}
/**
* 创建一个集合
*
* @param name 集合名
* @param options 集合配置信息
* @return 集合薄封装
*/
public MongoCollection<Document> createCollection(String name, CreateCollectionOptions options) {
if (this.collectionExists(name)) {
throw Lang.makeThrow("Colection has exists: %s.%s", db.getName(), name);
}
// 创建默认配置信息
if (null == options) {
options = new CreateCollectionOptions().capped(false);
}
db.createCollection(name, options);
return db.getCollection(name);
}
/**
* 判断集合是否存在
*
* @param collectionName 集合名
* @return
*/
public boolean collectionExists(String collectionName) {
return listCollectionNames().contains(collectionName);
}
/**
* @return 当前数据库所有可用集合名称
*/
public List<String> listCollectionNames() {
return db.listCollectionNames().into(new ArrayList<String>());
}
public MongoDatabase getNativeDB() {
return this.db;
}
}
初始化数据库示例
@Inject
private ZMongoDatabase zMongoDatabase;
public void init() {
CreateCollectionOptions collectionOptions = new CreateCollectionOptions();
TimeSeriesOptions timeSeriesOptions = new TimeSeriesOptions("ts");
timeSeriesOptions.metaField("metadata");
timeSeriesOptions.granularity(TimeSeriesGranularity.SECONDS);
collectionOptions.timeSeriesOptions(timeSeriesOptions);
MongoCollection<Document> deviceCollection = zMongoDatabase.createCollection("device", collectionOptions, true);
List<Document> list = new ArrayList<>();
Device device = new Device(Times.now(), 36.7, "0001");
list.add(new Document().append("ts", device.getTs()).append("temperature", device.getTemperature()).append("metadata",
new Document().append("no", device.getNo())));
Device device1 = new Device(Times.now(), 35.2, "0002");
list.add(new Document().append("ts", device1.getTs()).append("temperature", device1.getTemperature()).append("metadata",
new Document().append("no", device1.getNo())));
Device device2 = new Device(Times.now(), 10.7, "0002");
list.add(new Document().append("ts", device2.getTs()).append("temperature", device2.getTemperature()).append("metadata",
new Document().append("no", device2.getNo())));
Device device3 = new Device(Times.nextDay(Times.now(), 1), 30.0, "0002");
list.add(new Document().append("ts", device3.getTs()).append("temperature", device3.getTemperature()).append("metadata",
new Document().append("no", device3.getNo())));
// Document.parse(Json.toJson(new Device()));
InsertManyResult insertManyResult = deviceCollection.insertMany(list);
log.info(Json.toJson(insertManyResult));
}
时序数据求平均值示例
// 官方时序数据统计Demo https://docs.mongodb.com/manual/core/timeseries-collections/
@Ok("json:full")
@At("/avg")
public Object avg(@Param(value = "no", df = "0002") String no) {
List<Bson> bsons = new ArrayList<>();
// 筛选条件
Bson match = Aggregates.match(Filters.eq("metadata.no", no));
// 输出对象
Bson project = Aggregates.project(Projections.fields(
// 日期格式化
Projections.computed("date", new Document("$dateToParts", new Document().append("date", "$ts"))),
Projections.computed("temperature", 1)
));
// 分组统计 平均值
Bson group = Aggregates.group(new Document("date", new Document().append("year", "$date.year").append("month", "$date.month").append("day", "$date.day")),
Accumulators.avg("avgTemp", "$temperature")
);
bsons.add(match);
bsons.add(project);
bsons.add(group);
log.info(Json.toJson(bsons));
return zMongoDatabase.getCollection("device").aggregate(bsons);
}
完整代码:
nutzboot starter 组件
组件使用示例
nutzboot-demo-simple-mongodb-plus
i’am a separator…
1、el-tabs 中放置 el-tree 使用 this.$ref[‘tree’] 获取不到对象
使用 this.$ref[‘tree’][0] 可以取到
2、el-cascader 动态加载行政区划,修改表单时无法初始化选中的值
需要设置属性 :key=”xx” ,在数据加载完成后让 xx ++ 自加1
rpm -ivh TDengine-server-2.0.18.0-Linux-x64.rpm
vi /etc/taos/taos.cfg
加上当前服务器 hostname 主机名# first fully qualified domain name (FQDN) for TDengine system
firstEp wizzer-test:6030
# local fully qualified domain name (FQDN)
fqdn wizzer-test
taos
或 taos -h 127.0.0.1
执行数据库创建命令taos > create database test;
C:\Windows\System32\drivers\etc\hosts
ip wizzer-test
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.budwk</groupId>
<artifactId>test</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<nutzboot.version>2.4.2-SNAPSHOT</nutzboot.version>
<jaxb-api.version>2.3.1</jaxb-api.version>
<slf4j.version>1.7.25</slf4j.version>
<logback.version>1.2.3</logback.version>
<taos-jdbcdriver.version>2.0.23</taos-jdbcdriver.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.nutz</groupId>
<artifactId>nutzboot-core</artifactId>
</dependency>
<dependency>
<groupId>org.nutz</groupId>
<artifactId>nutzboot-starter-nutz-dao</artifactId>
</dependency>
<dependency>
<groupId>org.nutz</groupId>
<artifactId>nutzboot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>${taos-jdbcdriver.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.nutz</groupId>
<artifactId>nutzboot-parent</artifactId>
<version>${nutzboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>nutz</id>
<url>http://jfrog.nutz.cn/artifactory/libs-release</url>
</repository>
<repository>
<id>nutz-snapshots</id>
<url>http://jfrog.nutz.cn/artifactory/snapshots</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>nutz-snapshots</id>
<url>http://jfrog.nutz.cn/artifactory/snapshots</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.nutz.boot</groupId>
<artifactId>nutzboot-maven-plugin</artifactId>
<version>${nutzboot.version}</version>
</plugin>
</plugins>
</build>
</project>
/**
* 注意 TDengine 表及字段名都为小写字母
*/
@Table("iot_dev")
public class Iot_dev implements Serializable {
private static final long serialVersionUID = 1L;
@Column
@Comment("ID")
@ColDefine(type = ColType.TIMESTAMP)
private Date ts;
@Column("devid") //字段名都为小写字母
@Comment("设备 ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String devId;
@Column("devtype") //字段名都为小写字母
@Comment("设备类型")
@ColDefine(type = ColType.BINARY, width = 32)
private String devType;
@Column
@Comment("状态")
@ColDefine(type = ColType.BOOLEAN)
private Boolean status;
@Column
@Comment("读数 1")
@ColDefine(type = ColType.DOUBLE)
private Double val1;
@Column
@Comment("读数 2")
@ColDefine(type = ColType.INT)
private Integer val2;
@Column
@Comment("读数 3")
@ColDefine(type = ColType.INT,width = 3)
private Integer val3;
@Column
@Comment("读数 4")
@ColDefine(type = ColType.INT,width = 2)
private Integer val4;
}
本插件不同于V5代码生成器插件,无须引入项目中其他jar包,无须事先编译POJO类:
插件下载:
https://gitee.com/budwk/budwk-codegenerator/releases
插件源码:
1、终端设置代理
export http_proxy=socks5://127.0.0.1:1080
export https_proxy=socks5://127.0.0.1:1080
2、安装Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
3、Spotlight 索引占有CPU过高
关闭
sudo mdutil -a -i off
打开
sudo mdutil -a -i on
使用root权限修改文件
sudo vi ~/.bash_profile
vi 操作命令省略,插入如下内容
export MAVEN_HOME=/Users/wizzer/work/server/apache-maven-3.6.3
export PATH=$PATH:$MAVEN_HOME/bin
只读文件强制保存
:wq!
source ~/.bash_profile
解决新窗口和重启找不到mvn命令的问题:
touch ~/.zshrc
open ~/.zshrc
文件内容如下,command+S保存
source ~/.bash_profile
<el-cascader ref="area" v-model="infoForm.area" :props="cascaderProps" style="width: 500px;"
size="small" placeholder="请选择区域"></el-cascader>
cascaderProps: {
lazy: true,
value: 'id',
label: 'text',
lazyLoad: function (node, resolve) {
if (node.level === 0) {
var url = base + "/assets/platform/plugins/zoning/0.json";
$.get(url, function (d) {
resolve(d);
}, "json");
} else {
var pidaspath = node.data.id > 0 ? (node.data.id.substring(0, 2) + "/" + node.data.id) : 0;
var url = base + "/assets/platform/plugins/zoning/"+pidaspath+".json";
$.get(url, function (d) {
resolve(d);
}, "json");
}
}
},
infoForm: {
type: 'designer',
area: [],
areaCode: '',
areaText: '',
},
// 后台返回数据时
this.$set(this.infoForm,'area',this.infoForm.areaCode.split(','))
// 前端入库数据处理
if (this.infoForm.area) {
this.$set(this.infoForm, 'areaCode', this.infoForm.area.toString());
}
if (this.$refs['area']) {
var tree = this.$refs['area'].getCheckedNodes();
this.$set(this.infoForm, 'areaText', tree[0].pathLabels.toString());
}
File[] files=Files.lsDir(new File("D://node/zoning/dist/zoning-3"),"");
for(File dir:files){
if(Files.isDirectory(dir)){
File[] files2=Files.lsAll(dir.getAbsoluteFile(),"json");
for(File file:files2){
System.out.println(file.getAbsolutePath());
System.out.println(file.getName());
if(file.getName().length()==9||file.getName().equalsIgnoreCase("81.json")||file.getName().equalsIgnoreCase("82.json")){
String str=Files.read(file);
List<NutMap> list=Json.fromJsonAsList(NutMap.class,str);
List<NutMap> list2=new ArrayList<>();
for(NutMap nutMap:list){
nutMap.addv("leaf",true);
list2.add(nutMap);
}
Files.write(file,Json.toJson(list2, JsonFormat.compact()));
}
}
}
}
PS:临时用,不要纠结命名~~
pom.xml
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
生成验证码:
public NutMap getCode() {
String uuid = UUID.randomUUID().toString().replace("-", "");
ArithmeticCaptcha captcha = new ArithmeticCaptcha(120, 40);
captcha.getArithmeticString(); // 获取运算的公式:3+2=?
String text = captcha.text();
redisService.setex(RedisConstant.REDIS_CAPTCHA_KEY + uuid, 180, text);
return NutMap.NEW().addv("key", uuid).addv("codeUrl", captcha.toBase64());
}
验证验证码:(表单传递验证码及验证码key)
public void checkCode(String key, String code) throws CaptchaException {
String codeFromRedis = redisService.get(RedisConstant.REDIS_CAPTCHA_KEY + key);
if (Strings.isBlank(code)) {
throw new CaptchaException("请输入验证码");
}
if (Strings.isEmpty(codeFromRedis)) {
throw new CaptchaException("验证码已过期");
}
if (!Strings.equalsIgnoreCase(code, codeFromRedis)) {
throw new CaptchaException("验证码不正确");
}
redisService.del(RedisConstant.REDIS_SMSCODE_KEY + key);
}
try {
validateService.checkCode(key, code);
} catch (CaptchaException e) {
return Result.error(e.getMessage());
}
异常类:
public class CaptchaException extends Exception{
public CaptchaException(String message) {
super(message);
}
}
package com.budwk.app.base.utils;
import org.nutz.lang.util.NutMap;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
/**
* @author wizzer@qq.com
*/
public class FileUtil {
/**
* 分页获取文件列表
*
* @param basePath 目录
* @param pageNumber 页码
* @param pageSize 页大小
* @param sort 按文件名排序
* @return 列表
* @throws Exception
*/
public static NutMap readListPage(String basePath, Integer pageNumber, Integer pageSize, String sort)
throws Exception {
int offset = (pageNumber - 1) * pageSize;
int limit = pageNumber * pageSize;
long total = 0;
List<NutMap> list = new ArrayList<>();
Comparator<Path> comparator = Comparator.naturalOrder();
if ("desc".equals(sort)) {
comparator = Comparator.reverseOrder();
}
try (Stream<Path> fileList = Files.list(Paths.get(basePath))) {
total = fileList.count();
}
try (Stream<Path> fileList = Files.list(Paths.get(basePath)).sorted(comparator).skip(offset)
.limit(limit)) {
fileList.forEach(file -> {
NutMap nutMap = NutMap.NEW();
String fileName = file.getFileName().toString();
nutMap.addv("fileName", fileName);
if (Files.isDirectory(file.toAbsolutePath())) {
nutMap.addv("folder", true);
nutMap.addv("suffix", "folder");
} else {
String suffix = fileName.substring(fileName.indexOf(".") + 1).toLowerCase();
nutMap.addv("folder", false);
nutMap.addv("suffix", suffix);
}
list.add(nutMap);
});
return NutMap.NEW().addv("total", total).addv("list", list);
}
}
}
package com.budwk.nb.wx.models;
import com.budwk.nb.commons.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
/**
* 微信支付配置表
* @author wizzer@qq.com
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("wx_pay")
public class Wx_pay extends BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(els = {@EL("uuid()")})
private String id;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String name;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String mchid;
@Column
@ColDefine(type = ColType.VARCHAR, width = 50)
private String v3key;
/**
* apiclient_key.pem 物理路径
*/
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String v3keyPath;
/**
* apiclient_cert.pem 物理路径
*/
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String v3certPath;
/**
* apiclient_cert.p12 物理路径
*/
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String v3certP12Path;
/**
* 平台证书失效时间
*/
@Column
private Long expire_at;
}
package com.budwk.nb.wx.models;
import com.budwk.nb.commons.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
/**
* 平台证书临存表
* @author wizzer@qq.com
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("wx_pay_cert")
@TableIndexes({@Index(name = "INDEX_WX_PAY_CERT", fields = {"mchid", "serial_no"}, unique = true)})
public class Wx_pay_cert extends BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(els = {@EL("uuid()")})
private String id;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String mchid;
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String serial_no;
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String effective_time;
@Column
private Long effective_at;
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String expire_time;
@Column
private Long expire_at;
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String algorithm;
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String nonce;
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String associated_data;
@Column
@ColDefine(type = ColType.TEXT)
private String ciphertext;
@Column
@ColDefine(type = ColType.TEXT)
private String certificate;
}
package com.budwk.nb.web.commons.ext.wx;
import com.alibaba.dubbo.config.annotation.Reference;
import com.budwk.nb.web.commons.base.Globals;
import com.budwk.nb.wx.models.Wx_pay;
import com.budwk.nb.wx.models.Wx_pay_cert;
import com.budwk.nb.wx.services.WxPayCertService;
import com.budwk.nb.wx.services.WxPayService;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.weixin.bean.WxPay3Response;
import org.nutz.weixin.util.WxPay3Api;
import org.nutz.weixin.util.WxPay3Util;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
/**
* @author wizzer@qq.com
*/
@IocBean
public class WxPay3Service {
private static final Log log = Logs.get();
private static final SimpleDateFormat DATE_TIME_ZONE = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
@Inject
@Reference(check = false)
private WxPayCertService wxPayCertService;
@Inject
@Reference(check = false)
private WxPayService wxPayService;
// 通过商户号获取 wx_pay 对象
public synchronized Wx_pay getWxPay(String mchid) {
Wx_pay wxPay = Globals.WxPay3Map.getAs(mchid, Wx_pay.class);
if (wxPay == null) {
wxPay = wxPayService.fetch(Cnd.where("mchid", "=", mchid));
Globals.WxPay3Map.put(wxPay.getMchid(), wxPay);
}
checkPlatfromCerts(wxPay);
return wxPay;
}
// 检查及更新平台证书机制
public void checkPlatfromCerts(Wx_pay wxPay) {
if (wxPay == null)
throw new IllegalStateException("Wx_pay is null");
if (wxPay.getExpire_at() == null || wxPay.getExpire_at() == 0 || wxPay.getExpire_at() < 8 * 3600 * 1000 + System.currentTimeMillis()) {
getPlatfromCerts(wxPay.getMchid(), wxPay.getV3key(), wxPay.getV3keyPath(), wxPay.getV3certPath());
wxPay = wxPayService.fetch(Cnd.where("mchid", "=", wxPay.getMchid()));
Globals.WxPay3Map.put(wxPay.getMchid(), wxPay);
}
}
// jsapi 订单下单
public WxPay3Response v3_order_jsapi(String mchid, String body) throws Exception {
log.debug("v3_order_jsapi body::" + body);
String serialNo = WxPay3Util.getCertSerialNo(getWxPay(mchid).getV3certPath());
return WxPay3Api.v3_order_jsapi(mchid, serialNo, getWxPay(mchid).getV3keyPath(), body);
}
// 通过jsapi 订单号生成js参数
public NutMap v3_call_jsapi(String mchid, String appid, String prepay_id) throws Exception {
return WxPay3Util.getJsapiSignMessage(appid, prepay_id, getWxPay(mchid).getV3keyPath());
}
// 验证http响应签名结果
public boolean verifySignature(WxPay3Response wxPay3Response, String mchid) throws Exception {
Wx_pay_cert wxPayCert = wxPayCertService.fetch(Cnd.where("mchid", "=", mchid).and("serial_no", "=", wxPay3Response.getHeader().get("Wechatpay-Serial")));
return WxPay3Util.verifySignature(wxPay3Response, wxPayCert.getCertificate());
}
// 验证回调通知签名及内容
public String verifyNotify(String mchid, String serialNo, String body, String signature, String nonce, String timestamp) throws Exception {
Wx_pay_cert wxPayCert = wxPayCertService.fetch(Cnd.where("mchid", "=", mchid).and("serial_no", "=", serialNo));
return WxPay3Util.verifyNotify(serialNo, body, signature, nonce, timestamp,
getWxPay(mchid).getV3key(), wxPayCert.getCertificate());
}
/**
* 请求并保存新证书
*
* @param mchid
* @return
*/
public void getPlatfromCerts(String mchid, String v3Key, String v3KeyPatch, String v3CertPath) {
try {
wxPayCertService.clear(Cnd.where("mchid", "=", mchid).and("expire_at", "<", System.currentTimeMillis()));
String serialNo = WxPay3Util.getCertSerialNo(v3CertPath);
WxPay3Response wxPay3Response = WxPay3Api.v3_certificates(mchid, serialNo, v3KeyPatch);
if (wxPay3Response.getStatus() == 200) {
NutMap nutMap = Json.fromJson(NutMap.class, wxPay3Response.getBody());
List<NutMap> list = nutMap.getList("data", NutMap.class);
for (NutMap cert : list) {
Wx_pay_cert wxPayCert = new Wx_pay_cert();
wxPayCert.setMchid(mchid);
wxPayCert.setEffective_time(cert.getString("effective_time"));
wxPayCert.setExpire_time(cert.getString("expire_time"));
long expire_at = 0;
try {
expire_at = Times.parse(DATE_TIME_ZONE, cert.getString("expire_time")).getTime();
wxPayCert.setEffective_at(Times.parse(DATE_TIME_ZONE, cert.getString("effective_time")).getTime());
wxPayCert.setExpire_at(expire_at);
} catch (ParseException e) {
e.printStackTrace();
}
wxPayCert.setSerial_no(cert.getString("serial_no"));
NutMap encrypt_certificate = cert.getAs("encrypt_certificate", NutMap.class);
wxPayCert.setAlgorithm(encrypt_certificate.getString("algorithm"));
wxPayCert.setAssociated_data(encrypt_certificate.getString("associated_data"));
wxPayCert.setCiphertext(encrypt_certificate.getString("ciphertext"));
wxPayCert.setNonce(encrypt_certificate.getString("nonce"));
String platformCertificate = WxPay3Util.decryptToString(v3Key.getBytes(StandardCharsets.UTF_8),
encrypt_certificate.getString("associated_data").getBytes(StandardCharsets.UTF_8),
encrypt_certificate.getString("nonce").getBytes(StandardCharsets.UTF_8),
encrypt_certificate.getString("ciphertext")
);
wxPayCert.setCertificate(platformCertificate);
try {
wxPayCertService.insert(wxPayCert);
} catch (Exception e) {
//重复的插入会报错,不管它
}
}
Wx_pay_cert wxPayCert = wxPayCertService.fetch(Cnd.where("mchid", "=", mchid).orderBy("effective_at", "desc"));
if (wxPayCert != null) {
wxPayService.update(Chain.make("expire_at", wxPayCert.getExpire_at()), Cnd.where("mchid", "=", mchid));
}
}
} catch (Exception e) {
log.errorf("获取平台证书失败,mchid=%s", mchid, e);
}
}
}
@Test
public void test_v3_order() throws Exception {
String orderPayNo = R.UU32();
String orderId = R.UU32();
NutMap wxPayUnifiedOrder = NutMap.NEW();
wxPayUnifiedOrder.addv("appid", appid);
wxPayUnifiedOrder.addv("mchid", mchid);
wxPayUnifiedOrder.addv("description", new String(("LaiShop-order-" + orderId).getBytes(), StandardCharsets.UTF_8));
wxPayUnifiedOrder.addv("out_trade_no", orderPayNo);
Date now = new Date();
wxPayUnifiedOrder.addv("time_expire", DateUtil.getDateAfterMinute(now, 30));
// 回调通知URL传递mchid商户号,便于系统支持接入N个小程序及支付商户账号
wxPayUnifiedOrder.addv("notify_url", Globals.AppDomain + "/shop/open/wxpay/" + mchid + "/notify");
wxPayUnifiedOrder.addv("amount", NutMap.NEW().addv("total", 1).addv("currency", "CNY"));
wxPayUnifiedOrder.addv("payer", NutMap.NEW().addv("openid", "o9Bnd4lXKfNsOci-6H98zCMWyBps"));
String body = Json.toJson(wxPayUnifiedOrder);
System.out.println("body::" + body);
WxPay3Response wxPay3Response = wxPay3Service.v3_order_jsapi(mchid, body);
System.out.println("wxPay3Response::" + Json.toJson(wxPay3Response));
boolean verifySignature = wxPay3Service.verifySignature(wxPay3Response, mchid);
System.out.println("verifySignature::" + verifySignature);
NutMap v3order = Json.fromJson(NutMap.class, wxPay3Response.getBody());
NutMap resp = wxPay3Service.v3_call_jsapi(mchid, appid, v3order.getString("prepay_id"));
System.out.println("resp::" + Json.toJson(resp));
}
package com.budwk.nb.web.controllers.open.pay;
import com.alibaba.dubbo.config.annotation.Reference;
import com.budwk.nb.web.commons.ext.wx.WxPay3Service;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.servers.Server;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Streams;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.adaptor.VoidAdaptor;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
/**
* @author wizzer@qq.com
*/
@IocBean
@At("/shop/open/wxpay")
@Ok("json")
@OpenAPIDefinition(tags = {@Tag(name = "商城_微信支付回调")}, servers = @Server(url = "/"))
public class WxPay3NotifyController {
private static final Log log = Logs.get();
@Inject
private WxPay3Service wxPay3Service;
@At("/{mchid}/notify")
@Ok("raw")
@AdaptBy(type = VoidAdaptor.class)
public void notify(String mchid, Reader reader, HttpServletRequest req, HttpServletResponse resp) throws IOException {
try {
NutMap map = NutMap.NEW();
String timestamp = req.getHeader("Wechatpay-Timestamp");
String nonce = req.getHeader("Wechatpay-Nonce");
String serialNo = req.getHeader("Wechatpay-Serial");
String signature = req.getHeader("Wechatpay-Signature");
log.debugf("timestamp=%s,nonce=%s,serialNo=%s,signature=%s", timestamp, nonce, serialNo, signature);
String body = Streams.readAndClose(reader);
// 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
String plainText = wxPay3Service.verifyNotify(mchid, serialNo, body, signature, nonce, timestamp);
log.debugf("支付通知明文=%s", plainText);
NutMap res = Json.fromJson(NutMap.class, plainText);
NutMap payer = res.getAs("payer", NutMap.class);
String trade_state = res.getString("trade_state");
String out_trade_no = res.getString("out_trade_no");
String openid = payer.getString("openid");
boolean ok = true;//业务代码入库
if ("SUCCESS".equals(trade_state) && ok) {
resp.setStatus(200);
map.put("code", "SUCCESS");
map.put("message", "SUCCESS");
} else {
resp.setStatus(500);
map.put("code", "ERROR");
map.put("message", "签名错误");
}
resp.setHeader("Content-type", "application/json");
resp.getOutputStream().write(Json.toJson(map).getBytes(StandardCharsets.UTF_8));
resp.flushBuffer();
} catch (Exception e) {
e.printStackTrace();
}
}
}
本文档以 BudWk 框架代码为例,源码地址: https://gitee.com/budwk/budwk-nutzboot
演示地址: https://demo.budwk.com
为何要入手群晖呢……主要动机是因上段时间,电脑Windows 10 自动更新,重启后找不到SSD硬盘,硬盘数据完全丢失,痛定思痛下血本入手了群晖NAS,哭……
在入群晖之前家里已有软路由(J1900+8G内存+1T SSD),装了爱快+LEDE双系统,做下载机使用。软路由稳定运行一年多,性能尚可,但已无做黑裙等性能升级可能性,加之了解群晖主要的卖点是各类客户端软件,也省得自己折腾了。
DS220+ 双盘位
2G内存+免费升级至6G内存 + 酷狼8T硬盘 优惠后4300
PS:入手后才知道有个微信朋友有渠道,从他那买可以便宜500以上,啊啊啊
一块硬盘还是觉得不太安全(一块硬盘做不了raid1啊),但8T实在太贵,只好另加一块4T做备份用,至于这么做备份,后面软件章节介绍。
Moments – 私人相册,利用手机客户端同步手机相册及微信相册
Photo – 家庭相册,将精选的相片放到相册里,可以家人共享也可以分享给其他人
Drive – 核心应用,同步电脑及NAS文件夹
Video – 家庭媒体服务器,占用空间比较大,收藏了N年的电影/科幻片/动画片/周星驰/成龙/宫崎骏等
媒体服务器 – 提供DLNA服务,感觉没“网络邻居”共享文件夹来的速度快
Hyper Backup – 备份任务,将重要文件同步到第二块硬盘(没做raid1的情况下备份利器)
Cloud Sync – 同步百度云数据,百度云比较垃圾啊,又限速又经常断开要重新授权
Video 视频播放没有音频的问题:
详见教程 https://wp.gxnas.com/7561.html
自动安装是不可能的啦,首先要开SSH,获取root权限,然后手动安装ffmpeg
root教程 https://wp.gxnas.com/1385.html
后面配置自动获取电影封面和介绍,也需要root权限哦
Video 电影封面及介绍下载(自动或手动)问题:
更改hosts文件
vi /etc/hosts
31.13.70.9 api.themoviedb.org
噪音问题:
这款NAS风扇一点声音都没有,主要是硬盘读写有咯咯咯的声音,放客厅或弱电箱(靠近餐桌)都能明显听到,最后把它放阳台了,好在装修的时候阳台都预留了网口和电源,网购了一个4U机柜保护一下,防止浇花洒水等碰到。
Nacos 注册中心及配置中心功能微服务版本
Zookeeper 注册中心微服务版本
单应用一个 Jar 或 War 即可启动运行版本
后端技术:nutzboot(国产,基于国产nutz) + dubbo + redis + shiro + quartz + logback + beetl 等主流技术
前端技术:jquery + vue.js + elementUI 和 jquery + bootstrap 两个版本可选
V6演示地址: https://demo.budwk.com (前后端分离 nuxt+vue+elementUI)
V5演示地址: https://nutzwk.wizzer.cn (beetl+html+vue.js+elementUI)
NutzWk(V6起更名为BudWk)发展自2010年,2012年开始用于商业项目,至今已服务于全国各地公司大大小小上千个项目,行业涉及政务、电商、物联网等,随着经验积累及从事行业的不同分别发布了v1.x至v6.x多个版本,每个版本都是完整运行且完全开源免费的,您可以根据项目规模选择不同版本。本项目案例众多,省厅级项目、市级平台、大数据项目、电商平台、物联网平台等等,详见官网 https://budwk.com 。
V6源码地址: https://gitee.com/budwk/budwk-nutzboot
V1-V5源码地址: https://gitee.com/wizzer/NutzWk
CREATE USER 'budwk'@'localhost' IDENTIFIED BY '123'; GRANT ALL PRIVILEGES ON budwk_demo.* TO budwk@localhost; FLUSH PRIVILEGES;
项目介绍:
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 更新内容:
NutzWk v5.2.6-mini 版本发布:
NutzWk 5.2.0 版本已发布,演示地址: https://nutzwk.wizzer.cn
源码Github:https://github.com/Wizzercn/NutzWk
码云Gitee:https://gitee.com/wizzer/NutzWk
后端技术架构:nutzboot + dubbo + sentinel + redis + zookeeper
前端技术架构:vue.js + element.js
简述:
自 2018.03.20 发布 v5.0.1 第一个微服务分布式版本、2018.11.13 发布 v5.1.0 第一个Vue版本,一年来 NutzWk 根据项目实践及业务需要,逐步完善功能、修复bug、添加新特性,朝着“快速开发、功能丰富、扩展性强、性能优越”,在力所能及的情况下,最大限度的提高Web开发人员的生产力的目标继续前进。
随着项目越做越多,运维成了繁重的工作,本次 v5.2.0 周年版本主要带来了可在线上传jar包、编辑配置文件、关闭实例进程、启动新实例进程、动态修改日志等级、查看服务器资源占用情况等功能,支持分布式部署。详见:发行注记。