diff --git a/blade-common/pom.xml b/blade-common/pom.xml index 6c2eb10d69faea2f4acc02902af2f3e6da69baaf..06228ba0a37e4e0ec3b0489a35bd56b0305600a9 100644 --- a/blade-common/pom.xml +++ b/blade-common/pom.xml @@ -26,6 +26,25 @@ ${mica.auto.version} provided + + org.apache.commons + commons-lang3 + + + cn.hutool + hutool-all + 5.4.2 + + + com.google.guava + guava + 22.0 + + + com.baomidou + mybatis-plus-annotation + 3.4.0 + diff --git a/blade-common/src/main/java/org/springblade/common/baseEnum/BaseEnum.java b/blade-common/src/main/java/org/springblade/common/baseEnum/BaseEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..98ef1f83149f0ccb52ea756dded2ca1da5489b21 --- /dev/null +++ b/blade-common/src/main/java/org/springblade/common/baseEnum/BaseEnum.java @@ -0,0 +1,29 @@ +package org.springblade.common.baseEnum; + + +import com.baomidou.mybatisplus.annotation.IEnum; +import org.springblade.common.tool.MapHelper; + +import java.util.Arrays; +import java.util.Map; + +public interface BaseEnum extends IEnum { + static Map getMap(BaseEnum[] list) { + return MapHelper.uniqueIndex(Arrays.asList(list), BaseEnum::getCode, BaseEnum::getDesc); + } + + default String getCode() { + return this.toString(); + } + + String getDesc(); + + default boolean eq(String val) { + return this.getCode().equalsIgnoreCase(val); + } + + @Override + default String getValue() { + return this.getCode(); + } +} diff --git a/blade-common/src/main/java/org/springblade/common/constant/LauncherConstant.java b/blade-common/src/main/java/org/springblade/common/constant/LauncherConstant.java index abc21078000ab9cfc7b531e1060f68bba58ac7aa..e045101248d6f678df6a940b2a64a48ad5cca5d9 100644 --- a/blade-common/src/main/java/org/springblade/common/constant/LauncherConstant.java +++ b/blade-common/src/main/java/org/springblade/common/constant/LauncherConstant.java @@ -17,17 +17,17 @@ public interface LauncherConstant { /** * nacos dev 地址 */ - String NACOS_DEV_ADDR = "127.0.0.1:8848"; + String NACOS_DEV_ADDR = "47.110.236.215:8848"; /** * nacos prod 地址 */ - String NACOS_PROD_ADDR = "172.30.0.48:8848"; + String NACOS_PROD_ADDR = "47.110.236.215:8848"; /** * nacos test 地址 */ - String NACOS_TEST_ADDR = "172.30.0.48:8848"; + String NACOS_TEST_ADDR = "47.110.236.215:8848"; /** * sentinel dev 地址 diff --git a/blade-common/src/main/java/org/springblade/common/tool/MapHelper.java b/blade-common/src/main/java/org/springblade/common/tool/MapHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..266e695aa764b0d477bd23ed1a468ca1c204f560 --- /dev/null +++ b/blade-common/src/main/java/org/springblade/common/tool/MapHelper.java @@ -0,0 +1,81 @@ +package org.springblade.common.tool; + +import cn.hutool.core.collection.CollUtil; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.ImmutableMap; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.function.Function; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * map增强工具类 + * + * @author sp + */ +public class MapHelper { + + /** + * 增强 guava 的 Maps.uniqueIndex方法 + *

+ * guava 的 Maps.uniqueIndex方法可以实现: + *
+ * 将 list<V> 转成 Map<K , V> + * K 需要自己指定, V不能指定 + *

+ *

+ * 本方法实现了: + *

+ * 将 list<V> 转成 Map<K , M> + * K 需要自己指定, M需要自己指定 + * + * 其中K不能重复,若重复,则会报错 + *

+ * + * @param values 需要转换的集合 可以是任何实现了 Iterable 接口的集合(如List, Set, Collection) + * @param keyFunction 转换后Map的键的转换方式 + * @param valueFunction 转换后Map的值的转换方式 + * @param 转换后Map的键 类型 + * @param 转换前Iterable的迭代类型 + * @param 转换后Map的值 类型 + * @return + */ + public static ImmutableMap uniqueIndex(Iterable values, Function keyFunction, Function valueFunction) { + Iterator iterator = values.iterator(); + checkNotNull(keyFunction); + checkNotNull(valueFunction); + ImmutableMap.Builder builder = ImmutableMap.builder(); + while (iterator.hasNext()) { + V value = iterator.next(); + builder.put(keyFunction.apply(value), valueFunction.apply(value)); + } + try { + return builder.build(); + } catch (IllegalArgumentException duplicateKeys) { + throw new IllegalArgumentException( + duplicateKeys.getMessage() + + ".若要在键下索引多个值,请使用: Multimaps.index."); + } + } + + /** + * 转换 Map 的 K 和 V + * + * @param map + * @param + * @param + * @return + */ + public static Map inverse(Map map) { + if (CollUtil.isEmpty(map)) { + return Collections.emptyMap(); + } + HashBiMap biMap = HashBiMap.create(); + map.forEach(biMap::forcePut); + return biMap.inverse(); + } + +} diff --git a/blade-digital-api/digital-wallet-api/pom.xml b/blade-digital-api/digital-wallet-api/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..520ae42e7050a133ff2e67901ccfc17c73bba86d --- /dev/null +++ b/blade-digital-api/digital-wallet-api/pom.xml @@ -0,0 +1,18 @@ + + + + 4.0.0 + + blade-digital-api + org.springblade + 2.7.1 + + + digital-wallet-api + ${project.artifactId} + ${blade.project.version} + jar + + + diff --git a/blade-digital-api/digital-wallet-api/src/main/java/org/springblade/digital/wallet/dto/WalletRecordDTO.java b/blade-digital-api/digital-wallet-api/src/main/java/org/springblade/digital/wallet/dto/WalletRecordDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..892f567546e4440ef4a031e9da27dfd48a022d3b --- /dev/null +++ b/blade-digital-api/digital-wallet-api/src/main/java/org/springblade/digital/wallet/dto/WalletRecordDTO.java @@ -0,0 +1,18 @@ +package org.springblade.digital.wallet.dto; + +import org.springblade.digital.wallet.entity.WalletRecord; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 数据传输对象实体类 + * + * @author Blade + * @since 2020-07-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WalletRecordDTO extends WalletRecord { + private static final long serialVersionUID = 1L; + +} diff --git a/blade-digital-api/digital-wallet-api/src/main/java/org/springblade/digital/wallet/entity/WalletRecord.java b/blade-digital-api/digital-wallet-api/src/main/java/org/springblade/digital/wallet/entity/WalletRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..7d6d0fc2b080958f77faaea8ba5e21b4db7d4d11 --- /dev/null +++ b/blade-digital-api/digital-wallet-api/src/main/java/org/springblade/digital/wallet/entity/WalletRecord.java @@ -0,0 +1,215 @@ +package org.springblade.digital.wallet.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.springblade.core.mp.base.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; + +/** + * 实体类 + * + * @author Blade + * @since 2020-07-30 + */ +@Data +@TableName("wallet_record") +@EqualsAndHashCode(callSuper = true) +@ApiModel(value = "WalletRecord对象", description = "WalletRecord对象") +public class WalletRecord extends BaseEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @ApiModelProperty(value = "主键") + @TableId(value = "id", type = IdType.ASSIGN_ID) + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + /** + * dapp名称 + */ + @ApiModelProperty(value = "dapp名称") + private String dappName; + /** + * dapp下载地址 + */ + @ApiModelProperty(value = "dapp下载地址") + private String dappDownAddress; + /** + * dapp描述 + */ + @ApiModelProperty(value = "dapp描述") + private String dappDescribe; + /** + * 钱包名称 + */ + @ApiModelProperty(value = "钱包名称") + private String name; + /** + * 币种名称 + */ + @ApiModelProperty(value = "币种名称") + private String coinName; + /** + * 币种名称缩写 + */ + @ApiModelProperty(value = "币种名称缩写") + private String coinAbbr; + /** + * 钱包公钥地址 + */ + @ApiModelProperty(value = "钱包公钥地址") + private String puAddress; + /** + * 钱包私钥地址 + */ + @ApiModelProperty(value = "钱包私钥地址") + private String prAddress; + /** + * 钱包交易memo备注信息 + */ + @ApiModelProperty(value = "钱包交易memo备注信息") + private String memo; + /** + * 钱包钥匙备份信息 + */ + @ApiModelProperty(value = "钱包钥匙备份信息") + private String keyStore; + /** + * 钱包交易支付密码 + */ + @ApiModelProperty(value = "钱包交易支付密码") + private String payPassword; + /** + * 钱包助记词信息 + */ + @ApiModelProperty(value = "钱包助记词信息") + private String mnemonicWord; + /** + * 钱包助记词截图地址 + */ + @ApiModelProperty(value = "钱包助记词截图地址") + private String mnemonicWordPic; + +// public Long getId() { +// return id; +// } +// +// public void setId(Long id) { +// this.id = id; +// } +// +// public String getwDappName() { +// return wDappName; +// } +// +// public void setwDappName(String wDappName) { +// this.wDappName = wDappName; +// } +// +// public String getwDappDownAddress() { +// return wDappDownAddress; +// } +// +// public void setwDappDownAddress(String wDappDownAddress) { +// this.wDappDownAddress = wDappDownAddress; +// } +// +// public String getwDappDescribe() { +// return wDappDescribe; +// } +// +// public void setwDappDescribe(String wDappDescribe) { +// this.wDappDescribe = wDappDescribe; +// } +// +// public String getwName() { +// return wName; +// } +// +// public void setwName(String wName) { +// this.wName = wName; +// } +// +// public String getwCoinName() { +// return wCoinName; +// } +// +// public void setwCoinName(String wCoinName) { +// this.wCoinName = wCoinName; +// } +// +// public String getwCoinAbbr() { +// return wCoinAbbr; +// } +// +// public void setwCoinAbbr(String wCoinAbbr) { +// this.wCoinAbbr = wCoinAbbr; +// } +// +// public String getwPuAddress() { +// return wPuAddress; +// } +// +// public void setwPuAddress(String wPuAddress) { +// this.wPuAddress = wPuAddress; +// } +// +// public String getwPrAddress() { +// return wPrAddress; +// } +// +// public void setwPrAddress(String wPrAddress) { +// this.wPrAddress = wPrAddress; +// } +// +// public String getwMemo() { +// return wMemo; +// } +// +// public void setwMemo(String wMemo) { +// this.wMemo = wMemo; +// } +// +// public String getKeyStore() { +// return keyStore; +// } +// +// public void setKeyStore(String keyStore) { +// this.keyStore = keyStore; +// } +// +// public String getwPayPassword() { +// return wPayPassword; +// } +// +// public void setwPayPassword(String wPayPassword) { +// this.wPayPassword = wPayPassword; +// } +// +// public String getwMnemonicWord() { +// return wMnemonicWord; +// } +// +// public void setwMnemonicWord(String wMnemonicWord) { +// this.wMnemonicWord = wMnemonicWord; +// } +// +// public String getwMnemonicWordPic() { +// return wMnemonicWordPic; +// } +// +// public void setwMnemonicWordPic(String wMnemonicWordPic) { +// this.wMnemonicWordPic = wMnemonicWordPic; +// } +} diff --git a/blade-digital-api/digital-wallet-api/src/main/java/org/springblade/digital/wallet/vo/WalletRecordVO.java b/blade-digital-api/digital-wallet-api/src/main/java/org/springblade/digital/wallet/vo/WalletRecordVO.java new file mode 100644 index 0000000000000000000000000000000000000000..6ac6417d6034512445e0d4013a04c8264281a9a7 --- /dev/null +++ b/blade-digital-api/digital-wallet-api/src/main/java/org/springblade/digital/wallet/vo/WalletRecordVO.java @@ -0,0 +1,20 @@ +package org.springblade.digital.wallet.vo; + +import org.springblade.digital.wallet.entity.WalletRecord; +import lombok.Data; +import lombok.EqualsAndHashCode; +import io.swagger.annotations.ApiModel; + +/** + * 视图实体类 + * + * @author Blade + * @since 2020-07-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ApiModel(value = "WalletRecordVO对象", description = "WalletRecordVO对象") +public class WalletRecordVO extends WalletRecord { + private static final long serialVersionUID = 1L; + +} diff --git a/blade-digital-api/pom.xml b/blade-digital-api/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..11550c78fc6705f693c6fce1601420e8de1fb968 --- /dev/null +++ b/blade-digital-api/pom.xml @@ -0,0 +1,74 @@ + + + + 4.0.0 + + org.springblade + SpringBlade + 2.7.1 + + + blade-digital-api + ${project.artifactId} + 2.7.1 + pom + 数字资产微服务api集合 + + + digital-wallet-api + + + + org.springblade + blade-core-mybatis + ${blade.tool.version} + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + io.springfox + springfox-swagger2 + ${swagger.version} + + + io.swagger + swagger-models + + + + + io.swagger + swagger-models + ${swagger.models.version} + + + net.dreamlu + mica-auto + ${mica.auto.version} + provided + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + ${project.name} + + + + com.spotify + docker-maven-plugin + ${docker.plugin.version} + + true + + + + + diff --git a/blade-digital-service/digital-wallet/Dockerfile b/blade-digital-service/digital-wallet/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d92ffeead32f5eca86ea04c09cad51039233fa93 --- /dev/null +++ b/blade-digital-service/digital-wallet/Dockerfile @@ -0,0 +1,15 @@ +FROM anapsix/alpine-java:8_server-jre_unlimited + +MAINTAINER w1999wtw3537@sina.com + +RUN mkdir -p /digital/wallet + +WORKDIR /digital/wallet + +EXPOSE 8201 + +ADD ./target/digital-wallet.jar ./app.jar + +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] + +CMD ["--spring.profiles.active=test"] diff --git a/blade-digital-service/digital-wallet/pom.xml b/blade-digital-service/digital-wallet/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..4227cc671e5725ef9399cdd1a877fd897825701e --- /dev/null +++ b/blade-digital-service/digital-wallet/pom.xml @@ -0,0 +1,62 @@ + + + + 4.0.0 + + blade-digital-service + org.springblade + 2.7.1 + + + digital-wallet + ${project.artifactId} + ${blade.project.version} + jar + + + + + org.springblade + blade-core-boot + ${blade.tool.version} + + + org.springblade + digital-wallet-api + ${blade.tool.version} + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + package + + run + + + + + + + + + + + + + + diff --git a/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/WalletApplication.java b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/WalletApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..df295c64f44ddf9c5923e1e45ccdbf294b1ca6f0 --- /dev/null +++ b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/WalletApplication.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet; + +import org.springblade.core.launch.BladeApplication; +import org.springblade.core.launch.constant.AppConstant; +import org.springframework.cloud.client.SpringCloudApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * 系统模块启动器 + * @author Chill + */ +@SpringCloudApplication +@EnableFeignClients(AppConstant.BASE_PACKAGES) +public class WalletApplication { + // AppConstant.APPLICATION_SYSTEM_NAME + public static void main(String[] args) { + BladeApplication.run("digital-wallet", WalletApplication.class, args); + } + +} + diff --git a/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/controller/WalletRecordController.java b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/controller/WalletRecordController.java new file mode 100644 index 0000000000000000000000000000000000000000..0c9d4dab9a3f3b66f72cebc3c5ec3cd09e3eef43 --- /dev/null +++ b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/controller/WalletRecordController.java @@ -0,0 +1,112 @@ +package org.springblade.digital.wallet.controller; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.annotations.ApiParam; +import lombok.AllArgsConstructor; +import javax.validation.Valid; + +import org.springblade.core.mp.support.Condition; +import org.springblade.core.mp.support.Query; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.utils.Func; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.RequestParam; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.springblade.digital.wallet.entity.WalletRecord; +import org.springblade.digital.wallet.vo.WalletRecordVO; +import org.springblade.digital.wallet.wrapper.WalletRecordWrapper; +import org.springblade.digital.wallet.service.WalletRecordService; +import org.springblade.core.boot.ctrl.BladeController; + +/** + * 控制器 + * + * @author Blade + * @since 2020-07-30 + */ +@RestController +@AllArgsConstructor +@RequestMapping("/walletrecord") +@Api(value = "", tags = "接口") +public class WalletRecordController extends BladeController { + + private WalletRecordService walletRecordService; + + /** + * 详情 + */ + @GetMapping("/detail") + @ApiOperationSupport(order = 1) + @ApiOperation(value = "详情", notes = "传入walletRecord") + public R detail(WalletRecord walletRecord) { + WalletRecord detail = walletRecordService.getOne(Condition.getQueryWrapper(walletRecord)); + return R.data(WalletRecordWrapper.build().entityVO(detail)); + } + + /** + * 分页 + */ + @GetMapping("/list") + @ApiOperationSupport(order = 2) + @ApiOperation(value = "分页", notes = "传入walletRecord") + public R> list(WalletRecord walletRecord, Query query) { + IPage pages = walletRecordService.page(Condition.getPage(query), Condition.getQueryWrapper(walletRecord)); + return R.data(WalletRecordWrapper.build().pageVO(pages)); + } + + /** + * 自定义分页 + */ + @GetMapping("/page") + @ApiOperationSupport(order = 3) + @ApiOperation(value = "分页", notes = "传入walletRecord") + public R> page(WalletRecordVO walletRecord, Query query) { + IPage pages = walletRecordService.selectWalletRecordPage(Condition.getPage(query), walletRecord); + return R.data(pages); + } + + /** + * 新增 + */ + @PostMapping("/save") + @ApiOperationSupport(order = 4) + @ApiOperation(value = "新增", notes = "传入walletRecord") + public R save(@Valid @RequestBody WalletRecord walletRecord) { + return R.status(walletRecordService.save(walletRecord)); + } + + /** + * 修改 + */ + @PostMapping("/update") + @ApiOperationSupport(order = 5) + @ApiOperation(value = "修改", notes = "传入walletRecord") + public R update(@Valid @RequestBody WalletRecord walletRecord) { + return R.status(walletRecordService.updateById(walletRecord)); + } + + /** + * 新增或修改 + */ + @PostMapping("/submit") + @ApiOperationSupport(order = 6) + @ApiOperation(value = "新增或修改", notes = "传入walletRecord") + public R submit(@Valid @RequestBody WalletRecord walletRecord) { + return R.status(walletRecordService.saveOrUpdate(walletRecord)); + } + + + /** + * 删除 + */ + @PostMapping("/remove") + @ApiOperationSupport(order = 7) + @ApiOperation(value = "逻辑删除", notes = "传入ids") + public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) { + return R.status(walletRecordService.deleteLogic(Func.toLongList(ids))); + } + + +} diff --git a/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.java b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..aea19157096dcc6c4bddcfe2059dd284b33f9932 --- /dev/null +++ b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.mapper; + +import org.springblade.digital.wallet.entity.WalletRecord; +import org.springblade.digital.wallet.vo.WalletRecordVO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import java.util.List; + +/** + * Mapper 接口 + * + * @author Blade + * @since 2020-07-30 + */ +public interface WalletRecordMapper extends BaseMapper { + + /** + * 自定义分页 + * + * @param page + * @param walletRecord + * @return + */ + List selectWalletRecordPage(IPage page, WalletRecordVO walletRecord); + +} diff --git a/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.xml b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..5e32930e48485511853a963f7158cda6e1ba48c2 --- /dev/null +++ b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/service/WalletRecordService.java b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/service/WalletRecordService.java new file mode 100644 index 0000000000000000000000000000000000000000..90231b48f39f49492ecb080c53de99703a99b80b --- /dev/null +++ b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/service/WalletRecordService.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.service; + +import org.springblade.digital.wallet.entity.WalletRecord; +import org.springblade.digital.wallet.vo.WalletRecordVO; +import org.springblade.core.mp.base.BaseService; +import com.baomidou.mybatisplus.core.metadata.IPage; + +/** + * 服务类 + * + * @author Blade + * @since 2020-07-30 + */ +public interface WalletRecordService extends BaseService { + + /** + * 自定义分页 + * + * @param page + * @param walletRecord + * @return + */ + IPage selectWalletRecordPage(IPage page, WalletRecordVO walletRecord); + +} diff --git a/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/service/impl/WalletRecordServiceImpl.java b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/service/impl/WalletRecordServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..6bcd8d985d6aee875ccf6717472da4397e4913c2 --- /dev/null +++ b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/service/impl/WalletRecordServiceImpl.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.service.impl; + +import org.springblade.digital.wallet.entity.WalletRecord; +import org.springblade.digital.wallet.vo.WalletRecordVO; +import org.springblade.digital.wallet.mapper.WalletRecordMapper; +import org.springblade.digital.wallet.service.WalletRecordService; +import org.springblade.core.mp.base.BaseServiceImpl; +import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.core.metadata.IPage; + +/** + * 服务实现类 + * + * @author Blade + * @since 2020-07-30 + */ +@Service +public class WalletRecordServiceImpl extends BaseServiceImpl implements WalletRecordService { + + @Override + public IPage selectWalletRecordPage(IPage page, WalletRecordVO walletRecord) { + return page.setRecords(baseMapper.selectWalletRecordPage(page, walletRecord)); + } + +} diff --git a/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/wrapper/WalletRecordWrapper.java b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/wrapper/WalletRecordWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..d7398bc45ff42435c3430a428f3591d0dca50bf5 --- /dev/null +++ b/blade-digital-service/digital-wallet/src/main/java/org/springblade/digital/wallet/wrapper/WalletRecordWrapper.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.wrapper; + +import org.springblade.common.constant.CommonConstant; +import org.springblade.core.mp.support.BaseEntityWrapper; +import org.springblade.core.tool.node.ForestNodeMerger; +import org.springblade.core.tool.node.INode; +import org.springblade.core.tool.utils.BeanUtil; +import org.springblade.core.tool.utils.Func; +import org.springblade.core.tool.utils.SpringUtil; +import org.springblade.digital.wallet.entity.WalletRecord; +import org.springblade.digital.wallet.service.WalletRecordService; +import org.springblade.digital.wallet.vo.WalletRecordVO; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 包装类,返回视图层所需的字段 + * + * @author Chill + */ +public class WalletRecordWrapper extends BaseEntityWrapper { + + private static WalletRecordService walletRecordService; + + static { + walletRecordService = SpringUtil.getBean(WalletRecordService.class); + } + + public static WalletRecordWrapper build() { + return new WalletRecordWrapper(); + } + + @Override + public WalletRecordVO entityVO(WalletRecord walletRecord) { + WalletRecordVO walletRecordVO = BeanUtil.copy(walletRecord, WalletRecordVO.class); + + return walletRecordVO; + } + +} diff --git a/blade-digital-service/digital-wallet/src/main/resources/application-dev.yml b/blade-digital-service/digital-wallet/src/main/resources/application-dev.yml new file mode 100644 index 0000000000000000000000000000000000000000..a54bccc070f71271ac5760aebe68b3e966085d2e --- /dev/null +++ b/blade-digital-service/digital-wallet/src/main/resources/application-dev.yml @@ -0,0 +1,11 @@ +#服务器端口 +server: + port: 8201 + +#数据源配置 +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: ${blade.datasource.dev.url} + username: ${blade.datasource.dev.username} + password: ${blade.datasource.dev.password} diff --git a/blade-digital-service/digital-wallet/src/main/resources/application-prod.yml b/blade-digital-service/digital-wallet/src/main/resources/application-prod.yml new file mode 100644 index 0000000000000000000000000000000000000000..25635bc41de5f77481e3d6ea0e52cf9444e30137 --- /dev/null +++ b/blade-digital-service/digital-wallet/src/main/resources/application-prod.yml @@ -0,0 +1,10 @@ +#服务器端口 +server: + port: 8106 + +#数据源配置 +spring: + datasource: + url: ${blade.datasource.prod.url} + username: ${blade.datasource.prod.username} + password: ${blade.datasource.prod.password} diff --git a/blade-digital-service/digital-wallet/src/main/resources/application-test.yml b/blade-digital-service/digital-wallet/src/main/resources/application-test.yml new file mode 100644 index 0000000000000000000000000000000000000000..fb5cd8f7318cf5a3ba05543b595ac6b3a600883d --- /dev/null +++ b/blade-digital-service/digital-wallet/src/main/resources/application-test.yml @@ -0,0 +1,10 @@ +#服务器端口 +server: + port: 8106 + +#数据源配置 +spring: + datasource: + url: ${blade.datasource.test.url} + username: ${blade.datasource.test.username} + password: ${blade.datasource.test.password} diff --git a/blade-digital-service/pom.xml b/blade-digital-service/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..75c91630f0f0db7a4d3968b160dc4a775fcbf5ae --- /dev/null +++ b/blade-digital-service/pom.xml @@ -0,0 +1,52 @@ + + + + 4.0.0 + + org.springblade + SpringBlade + 2.7.1 + + + blade-digital-service + ${project.artifactId} + 2.7.1 + pom + 数字资产微服务集合 + + digital-wallet + + + + org.springblade + blade-common + ${blade.project.version} + + + + + + + com.spotify + docker-maven-plugin + ${docker.plugin.version} + + ${docker.registry.url}/blade/${project.artifactId}:${project.version} + ${project.basedir} + ${docker.registry.host} + + + / + ${project.build.directory} + ${project.build.finalName}.jar + + + ${docker.registry.url} + ${docker.registry.url} + true + + + + + diff --git a/blade-gateway/src/main/resources/bootstrap.yml b/blade-gateway/src/main/resources/bootstrap.yml index e875240d64ebab8a212a87e98d0ac08541bddcc8..f2ee4f22500c2a601cf6a1be48883de35384a8dd 100644 --- a/blade-gateway/src/main/resources/bootstrap.yml +++ b/blade-gateway/src/main/resources/bootstrap.yml @@ -13,7 +13,6 @@ spring: loadbalancer: retry: enabled: true - # 聚合文档配置 blade: document: diff --git a/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/controller/WalletRecordController.java b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/controller/WalletRecordController.java new file mode 100644 index 0000000000000000000000000000000000000000..86a89d6783ed9d3de37d1ed54af1abf7a01a001d --- /dev/null +++ b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/controller/WalletRecordController.java @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.controller; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.annotations.ApiParam; +import lombok.AllArgsConstructor; +import javax.validation.Valid; + +import org.springblade.core.mp.support.Condition; +import org.springblade.core.mp.support.Query; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.utils.Func; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.RequestParam; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.springblade.digital.wallet.entity.WalletRecord; +import org.springblade.digital.wallet.vo.WalletRecordVO; +import org.springblade.digital.wallet.wrapper.WalletRecordWrapper; +import org.springblade.digital.wallet.service.IWalletRecordService; +import org.springblade.core.boot.ctrl.BladeController; +import java.util.List; + +/** + * 控制器 + * + * @author Blade + * @since 2020-08-13 + */ +@RestController +@AllArgsConstructor +@RequestMapping("/walletrecord") +@Api(value = "", tags = "接口") +public class WalletRecordController extends BladeController { + + private IWalletRecordService walletRecordService; + + /** + * 详情 + */ + @GetMapping("/detail") + @ApiOperationSupport(order = 1) + @ApiOperation(value = "详情", notes = "传入walletRecord") + public R detail(WalletRecord walletRecord) { + WalletRecord detail = walletRecordService.getOne(Condition.getQueryWrapper(walletRecord)); + return R.data(WalletRecordWrapper.build().entityVO(detail)); + } + + /** + * 分页 + */ + @GetMapping("/list") + @ApiOperationSupport(order = 2) + @ApiOperation(value = "分页", notes = "传入walletRecord") + public R> list(WalletRecord walletRecord, Query query) { + IPage pages = walletRecordService.page(Condition.getPage(query), Condition.getQueryWrapper(walletRecord)); + return R.data(WalletRecordWrapper.build().pageVO(pages)); + } + + /** + * 自定义分页 + */ + @GetMapping("/page") + @ApiOperationSupport(order = 3) + @ApiOperation(value = "分页", notes = "传入walletRecord") + public R> page(WalletRecordVO walletRecord, Query query) { + IPage pages = walletRecordService.selectWalletRecordPage(Condition.getPage(query), walletRecord); + return R.data(pages); + } + + /** + * 新增 + */ + @PostMapping("/save") + @ApiOperationSupport(order = 4) + @ApiOperation(value = "新增", notes = "传入walletRecord") + public R save(@Valid @RequestBody WalletRecord walletRecord) { + return R.status(walletRecordService.save(walletRecord)); + } + + /** + * 修改 + */ + @PostMapping("/update") + @ApiOperationSupport(order = 5) + @ApiOperation(value = "修改", notes = "传入walletRecord") + public R update(@Valid @RequestBody WalletRecord walletRecord) { + return R.status(walletRecordService.updateById(walletRecord)); + } + + /** + * 新增或修改 + */ + @PostMapping("/submit") + @ApiOperationSupport(order = 6) + @ApiOperation(value = "新增或修改", notes = "传入walletRecord") + public R submit(@Valid @RequestBody WalletRecord walletRecord) { + return R.status(walletRecordService.saveOrUpdate(walletRecord)); + } + + + /** + * 删除 + */ + @PostMapping("/remove") + @ApiOperationSupport(order = 7) + @ApiOperation(value = "逻辑删除", notes = "传入ids") + public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) { + return R.status(walletRecordService.deleteLogic(Func.toLongList(ids))); + } + + +} diff --git a/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/dto/WalletRecordDTO.java b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/dto/WalletRecordDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..6e0338bfd96998f91e76fe56adc5c0e88e27b80f --- /dev/null +++ b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/dto/WalletRecordDTO.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.dto; + +import org.springblade.digital.wallet.entity.WalletRecord; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 数据传输对象实体类 + * + * @author Blade + * @since 2020-08-13 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WalletRecordDTO extends WalletRecord { + private static final long serialVersionUID = 1L; + +} diff --git a/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/entity/WalletRecord.java b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/entity/WalletRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..ad4a81bbfad7c6c668598f67ca0a670d5786288d --- /dev/null +++ b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/entity/WalletRecord.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import org.springblade.core.mp.base.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * 实体类 + * + * @author Blade + * @since 2020-08-13 + */ +@Data +@TableName("wallet_record") +@EqualsAndHashCode(callSuper = true) +@ApiModel(value = "WalletRecord对象", description = "WalletRecord对象") +public class WalletRecord extends BaseEntity { + + private static final long serialVersionUID = 1L; + + private Long id; + /** + * dapp名称 + */ + @ApiModelProperty(value = "dapp名称") + private String wDappName; + /** + * dapp下载地址 + */ + @ApiModelProperty(value = "dapp下载地址") + private String wDappDownAddress; + /** + * dapp描述 + */ + @ApiModelProperty(value = "dapp描述") + private String wDappDescribe; + /** + * 钱包名称 + */ + @ApiModelProperty(value = "钱包名称") + private String wName; + /** + * 币种名称 + */ + @ApiModelProperty(value = "币种名称") + private String wCoinName; + /** + * 币种名称缩写 + */ + @ApiModelProperty(value = "币种名称缩写") + private String wCoinAbbr; + /** + * 钱包公钥地址 + */ + @ApiModelProperty(value = "钱包公钥地址") + private String wPuAddress; + /** + * 钱包私钥地址 + */ + @ApiModelProperty(value = "钱包私钥地址") + private String wPrAddress; + /** + * 钱包交易memo备注信息 + */ + @ApiModelProperty(value = "钱包交易memo备注信息") + private String wMemo; + /** + * 钱包钥匙备份信息 + */ + @ApiModelProperty(value = "钱包钥匙备份信息") + private String keyStore; + /** + * 钱包交易支付密码 + */ + @ApiModelProperty(value = "钱包交易支付密码") + private String wPayPassword; + /** + * 钱包助记词信息 + */ + @ApiModelProperty(value = "钱包助记词信息") + private String wMnemonicWord; + /** + * 钱包助记词截图地址 + */ + @ApiModelProperty(value = "钱包助记词截图地址") + private String wMnemonicWordPic; + + +} diff --git a/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.java b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..5a44681647601ca8800d120728d8c825c0df0fcf --- /dev/null +++ b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.mapper; + +import org.springblade.digital.wallet.entity.WalletRecord; +import org.springblade.digital.wallet.vo.WalletRecordVO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import java.util.List; + +/** + * Mapper 接口 + * + * @author Blade + * @since 2020-08-13 + */ +public interface WalletRecordMapper extends BaseMapper { + + /** + * 自定义分页 + * + * @param page + * @param walletRecord + * @return + */ + List selectWalletRecordPage(IPage page, WalletRecordVO walletRecord); + +} diff --git a/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.xml b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..0d6d73cc0f93cc0819ea747cc2cc67472dc8d9c4 --- /dev/null +++ b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/mapper/WalletRecordMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/service/IWalletRecordService.java b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/service/IWalletRecordService.java new file mode 100644 index 0000000000000000000000000000000000000000..af7e5688833191e65b0daf712ce4fa93e43ef539 --- /dev/null +++ b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/service/IWalletRecordService.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.service; + +import org.springblade.digital.wallet.entity.WalletRecord; +import org.springblade.digital.wallet.vo.WalletRecordVO; +import org.springblade.core.mp.base.BaseService; +import com.baomidou.mybatisplus.core.metadata.IPage; + +/** + * 服务类 + * + * @author Blade + * @since 2020-08-13 + */ +public interface IWalletRecordService extends BaseService { + + /** + * 自定义分页 + * + * @param page + * @param walletRecord + * @return + */ + IPage selectWalletRecordPage(IPage page, WalletRecordVO walletRecord); + +} diff --git a/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/service/impl/WalletRecordServiceImpl.java b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/service/impl/WalletRecordServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..6081effd0092b45ab14fcf321206d5de69ef7ef0 --- /dev/null +++ b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/service/impl/WalletRecordServiceImpl.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.service.impl; + +import org.springblade.digital.wallet.entity.WalletRecord; +import org.springblade.digital.wallet.vo.WalletRecordVO; +import org.springblade.digital.wallet.mapper.WalletRecordMapper; +import org.springblade.digital.wallet.service.IWalletRecordService; +import org.springblade.core.mp.base.BaseServiceImpl; +import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.core.metadata.IPage; + +/** + * 服务实现类 + * + * @author Blade + * @since 2020-08-13 + */ +@Service +public class WalletRecordServiceImpl extends BaseServiceImpl implements IWalletRecordService { + + @Override + public IPage selectWalletRecordPage(IPage page, WalletRecordVO walletRecord) { + return page.setRecords(baseMapper.selectWalletRecordPage(page, walletRecord)); + } + +} diff --git a/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/vo/WalletRecordVO.java b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/vo/WalletRecordVO.java new file mode 100644 index 0000000000000000000000000000000000000000..85c12c586afc3b002ba13a9a672f13eefbef1976 --- /dev/null +++ b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/org/springblade/digital/wallet/vo/WalletRecordVO.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.digital.wallet.vo; + +import org.springblade.digital.wallet.entity.WalletRecord; +import lombok.Data; +import lombok.EqualsAndHashCode; +import io.swagger.annotations.ApiModel; + +/** + * 视图实体类 + * + * @author Blade + * @since 2020-08-13 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ApiModel(value = "WalletRecordVO对象", description = "WalletRecordVO对象") +public class WalletRecordVO extends WalletRecord { + private static final long serialVersionUID = 1L; + +} diff --git a/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/sql/walletrecord.menu.mysql b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/sql/walletrecord.menu.mysql new file mode 100644 index 0000000000000000000000000000000000000000..75014d5c57c3e36d2e3b505aa72ac3b2168af211 --- /dev/null +++ b/blade-ops/blade-develop/blade-ops/blade-develop/src/main/java/sql/walletrecord.menu.mysql @@ -0,0 +1,11 @@ +INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`) +VALUES (0, 'walletrecord', '钱包信息记录管理', 'menu', '/wallet/walletrecord', NULL, 1, 1, 0, 1, NULL, 0); +set @parentid = (SELECT LAST_INSERT_ID()); +INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`) +VALUES (@parentid, 'walletrecord_add', '新增', 'add', '/wallet/walletrecord/add', 'plus', 1, 2, 1, 1, NULL, 0); +INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`) +VALUES (@parentid, 'walletrecord_edit', '修改', 'edit', '/wallet/walletrecord/edit', 'form', 2, 2, 1, 2, NULL, 0); +INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`) +VALUES (@parentid, 'walletrecord_delete', '删除', 'delete', '/api/digital-wallet/walletrecord/remove', 'delete', 3, 2, 1, 3, NULL, 0); +INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`) +VALUES (@parentid, 'walletrecord_view', '查看', 'view', '/wallet/walletrecord/view', 'file-text', 4, 2, 1, 2, NULL, 0); diff --git a/blade-ops/blade-develop/pom.xml b/blade-ops/blade-develop/pom.xml index eb93f7cc972f57f5d396f21862ac241caaf59234..d642600253fe31986fe1a63d455d5d9194ab4db2 100644 --- a/blade-ops/blade-develop/pom.xml +++ b/blade-ops/blade-develop/pom.xml @@ -55,6 +55,12 @@ blade-dict-api ${blade.project.version} + + org.springblade + blade-core-test + ${blade.tool.version} + test + diff --git a/blade-ops/blade-develop/src/test/java/org/springblade/test/CodeGenerator.java b/blade-ops/blade-develop/src/test/java/org/springblade/test/CodeGenerator.java index 6cb84af709a71479c13141fe4927b2de4216b6a1..2f994eba5fb51a1c43b522868277888182f9a3a2 100644 --- a/blade-ops/blade-develop/src/test/java/org/springblade/test/CodeGenerator.java +++ b/blade-ops/blade-develop/src/test/java/org/springblade/test/CodeGenerator.java @@ -23,66 +23,10 @@ import org.springblade.develop.support.BladeCodeGenerator; * * @author Chill */ -public class CodeGenerator { +public interface CodeGenerator { - /** - * 代码生成的模块名 - */ - public static String CODE_NAME = "租户管理"; - /** - * 代码所在服务名 - */ - public static String SERVICE_NAME = "blade-system"; - /** - * 代码生成的包名 - */ - public static String PACKAGE_NAME = "org.springblade.system"; - /** - * 前端代码生成所属系统 - */ - public static String SYSTEM_NAME = "sword"; - /** - * 前端代码生成地址 - */ - public static String PACKAGE_WEB_DIR = "/Users/chill/Workspaces/product/Sword"; - /** - * 需要去掉的表前缀 - */ - public static String[] TABLE_PREFIX = {"blade_"}; - /** - * 需要生成的表名(两者只能取其一) - */ - public static String[] INCLUDE_TABLES = {"blade_tenant"}; - /** - * 需要排除的表名(两者只能取其一) - */ - public static String[] EXCLUDE_TABLES = {}; - /** - * 是否包含基础业务字段 - */ - public static Boolean HAS_SUPER_ENTITY = Boolean.TRUE; - /** - * 基础业务字段 - */ - public static String[] SUPER_ENTITY_COLUMNS = {"create_time", "create_user", "update_time", "update_user", "status", "is_deleted"}; + // 初始化代码生成器 + public BladeCodeGenerator initCodeGenerator(CodeModel codeModel); - /** - * RUN THIS - */ - public static void main(String[] args) { - BladeCodeGenerator generator = new BladeCodeGenerator(); - generator.setCodeName(CODE_NAME); - generator.setServiceName(SERVICE_NAME); - generator.setSystemName(SYSTEM_NAME); - generator.setPackageName(PACKAGE_NAME); - generator.setPackageWebDir(PACKAGE_WEB_DIR); - generator.setTablePrefix(TABLE_PREFIX); - generator.setIncludeTables(INCLUDE_TABLES); - generator.setExcludeTables(EXCLUDE_TABLES); - generator.setHasSuperEntity(HAS_SUPER_ENTITY); - generator.setSuperEntityColumns(SUPER_ENTITY_COLUMNS); - generator.run(); - } - } diff --git a/blade-ops/blade-develop/src/test/java/org/springblade/test/CodeGeneratorTest.java b/blade-ops/blade-develop/src/test/java/org/springblade/test/CodeGeneratorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8669086a0e8e01943fa9f2a917a451627341b4ca --- /dev/null +++ b/blade-ops/blade-develop/src/test/java/org/springblade/test/CodeGeneratorTest.java @@ -0,0 +1,22 @@ +package org.springblade.test; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springblade.core.launch.constant.AppConstant; +import org.springblade.core.test.BladeBootTest; +import org.springblade.core.test.BladeSpringRunner; +import org.springblade.develop.DevelopApplication; +import org.springframework.boot.test.context.SpringBootTest; + +@RunWith(BladeSpringRunner.class) +@SpringBootTest(classes = DevelopApplication.class) +@BladeBootTest(appName = AppConstant.APPLICATION_DEVELOP_NAME, profile = "dev", enableLoader = true) +public class CodeGeneratorTest { + + @Test + public void walletCodeGeneratorTest(){ + WalletCode walletCode = new WalletCode(); + walletCode.initCodeGenerator(null).run(); + } + +} diff --git a/blade-ops/blade-develop/src/test/java/org/springblade/test/CodeModel.java b/blade-ops/blade-develop/src/test/java/org/springblade/test/CodeModel.java new file mode 100644 index 0000000000000000000000000000000000000000..7a34541fbf5e48d1820fa8bd26b3d4bbcc19f894 --- /dev/null +++ b/blade-ops/blade-develop/src/test/java/org/springblade/test/CodeModel.java @@ -0,0 +1,51 @@ +package org.springblade.test; + +import lombok.Data; + +@Data +public class CodeModel { + + /** + * 基础业务字段 + */ + public static String[] superEntityColumns = {"create_time", "create_user", "update_time", "update_user", "status", "is_deleted"}; + + /** + * 代码生成的模块名 + */ + private String codeName; + /** + * 代码所在服务名 + */ + private String serviceName; + /** + * 代码生成的包名 + */ + private String packageName; + /** + * 前端代码生成所属系统 + */ + private String systemName; + /** + * 前端代码生成地址 + */ + private String packageWebDir; + /** + * 需要去掉的表前缀 + */ + private String[] tablePrefix; + /** + * 需要生成的表名(两者只能取其一) + */ + private String[] includeTables; + /** + * 需要排除的表名(两者只能取其一) + */ + private String[] excludeTables; + /** + * 是否包含基础业务字段 + */ + private Boolean hasSuperEntity; + + +} diff --git a/blade-ops/blade-develop/src/test/java/org/springblade/test/WalletCode.java b/blade-ops/blade-develop/src/test/java/org/springblade/test/WalletCode.java new file mode 100644 index 0000000000000000000000000000000000000000..f1ee2f000d947a7b1c0bf286633d99971aec86fd --- /dev/null +++ b/blade-ops/blade-develop/src/test/java/org/springblade/test/WalletCode.java @@ -0,0 +1,47 @@ +package org.springblade.test; + +import org.springblade.develop.support.BladeCodeGenerator; + +public class WalletCode implements CodeGenerator{ + + private CodeModel codeModel; + + public WalletCode(){ + CodeModel codeModel = new CodeModel(); + codeModel.setCodeName("钱包信息记录管理"); + codeModel.setServiceName("digital-wallet"); + codeModel.setSystemName("sword"); + codeModel.setPackageName("org.springblade.digital.wallet"); + codeModel.setPackageWebDir("D://Workspaces/digital/Sword"); + codeModel.setTablePrefix(new String[]{""}); + codeModel.setIncludeTables(new String[]{"wallet_record"}); +// codeModel.setExcludeTables(new String[]{""}); + codeModel.setHasSuperEntity(Boolean.TRUE); + this.codeModel = codeModel; + } + // 初始化代码生成器 + public BladeCodeGenerator initCodeGenerator(CodeModel codeModel){ + + if(codeModel == null){ + codeModel = this.codeModel; + } + + BladeCodeGenerator generator = new BladeCodeGenerator(); + + generator.setCodeName(codeModel.getCodeName()); + generator.setServiceName(codeModel.getServiceName()); + generator.setSystemName(codeModel.getSystemName()); + generator.setPackageName(codeModel.getPackageName()); + generator.setPackageWebDir(codeModel.getPackageWebDir()); + generator.setTablePrefix(codeModel.getTablePrefix()); + generator.setIncludeTables(codeModel.getIncludeTables()); + // IncludeTables 与ExcludeTables 策略不能同时存在 +// generator.setExcludeTables(codeModel.getExcludeTables()); + generator.setHasSuperEntity(codeModel.getHasSuperEntity()); + generator.setSuperEntityColumns(codeModel.superEntityColumns); + + return generator; + } + + +} diff --git a/blade-service-api/blade-file-api/pom.xml b/blade-service-api/blade-file-api/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..25cf6cd124bf7b66d0d22ec4d1f006878c3001d8 --- /dev/null +++ b/blade-service-api/blade-file-api/pom.xml @@ -0,0 +1,23 @@ + + + + + blade-service-api + org.springblade + 2.7.1 + + 4.0.0 + blade-file-api + ${project.artifactId} + ${blade.project.version} + jar + + + + org.springblade + blade-common + 2.7.1 + + + diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/constant/FileConstants.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/constant/FileConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..e29012a347acffb2b8f068d0c6af7aa7057178e1 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/constant/FileConstants.java @@ -0,0 +1,51 @@ +package org.springblade.file.constant; + + +import java.io.Serializable; + +/** + *

+ * 数据库常量 + * 文件表 + *

+ * + * @author zt + * @date 2020-09-10 + */ + +public class FileConstants implements Serializable { + + /** + * 字段常量 + */ + public static final String ID = "id"; + public static final String DATA_TYPE = "data_type"; + public static final String SUBMITTED_FILE_NAME = "submitted_file_name"; + public static final String TREE_PATH = "tree_path"; + public static final String GRADE = "grade"; + public static final String IS_DELETE = "is_delete"; + public static final String FOLDER_ID = "folder_id"; + public static final String URL = "url"; + public static final String SIZE = "size"; + public static final String FOLDER_NAME = "folder_name"; + public static final String GROUP = "group_"; + public static final String PATH = "path"; + public static final String RELATIVE_PATH = "relative_path"; + public static final String FILE_MD5 = "file_md5"; + public static final String CONTEXT_TYPE = "context_type"; + public static final String FILENAME = "filename"; + public static final String EXT = "ext"; + public static final String ICON = "icon"; + public static final String CREATE_MONTH = "create_month"; + public static final String CREATE_WEEK = "create_week"; + public static final String CREATE_DAY = "create_day"; + public static final String CREATE_TIME = "create_time"; + public static final String CREATE_USER = "create_user"; + public static final String UPDATE_TIME = "update_time"; + public static final String UPDATE_USER = "update_user"; + private static final long serialVersionUID = 1L; + + private FileConstants() { + super(); + } +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileAttrDO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileAttrDO.java new file mode 100644 index 0000000000000000000000000000000000000000..289e80fbb192bb65957032cf4ecca890552bba7c --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileAttrDO.java @@ -0,0 +1,35 @@ +package org.springblade.file.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 文件部分属性 + * + * @author ds + */ +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Data +public class FileAttrDO { + /** + * 路径 + */ + private String treePath; + /** + * 层级 + */ + private Integer grade; + /** + * 文件夹名称 + */ + private String folderName; + /** + * 文件夹id + */ + private Long folderId; + +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileDO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileDO.java new file mode 100644 index 0000000000000000000000000000000000000000..9b1ea587a6924d1f9e4aea5c3dc73c953b5c5060 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileDO.java @@ -0,0 +1,33 @@ +package org.springblade.file.domain; + + +import lombok.Builder; +import lombok.Data; +import org.springblade.file.enumeration.DataType; + +/** + * 文件、附件DO + * + * @author zuihou + * @date 2019/05/06 + */ +@Data +@Builder +public class FileDO { + /** + * 原始文件名 + */ + private String submittedFileName; + /** + * 数据类型 IMAGE/VIDEO/AUDIO/DOC/OTHER/DIR + */ + private DataType dataType; + /** + * 文件访问链接 + */ + private String url; + /** + * 文件大小 + */ + private Long size; +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileDeleteDO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileDeleteDO.java new file mode 100644 index 0000000000000000000000000000000000000000..5a3d194961f7f7037bb519d3c6430abdc2110bdf --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileDeleteDO.java @@ -0,0 +1,36 @@ +package org.springblade.file.domain; + +import lombok.Builder; +import lombok.Data; + +/** + * 文件删除 + * + * @author zuihou + * @date 2019/05/07 + */ +@Data +@Builder +public class FileDeleteDO { + /** + * fastDFS返回的组 用于FastDFS + */ + private String group; + /** + * fastdfs 的路径 + */ + private String path; + /** + * 唯一文件名 + */ + private String fileName; + /** + * 文件在服务器的绝对路径 + */ + private String relativePath; + private Long id; + /** + * 是否是云盘文件删除 + */ + private Boolean file; +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileQueryDO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileQueryDO.java new file mode 100644 index 0000000000000000000000000000000000000000..f6060f0fa04cb8a1e56f62e14994cbb561df0885 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileQueryDO.java @@ -0,0 +1,17 @@ +package org.springblade.file.domain; + + +import lombok.Data; +import org.springblade.file.entity.File; + +/** + * 文件查询 DO + * + * @author zuihou + * @date 2019/05/07 + */ +@Data +public class FileQueryDO extends File { + private File parent; + +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileStatisticsDO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileStatisticsDO.java new file mode 100644 index 0000000000000000000000000000000000000000..6097480a61d67e800656282e5832e3650815d723 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/domain/FileStatisticsDO.java @@ -0,0 +1,37 @@ +package org.springblade.file.domain; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springblade.file.enumeration.DataType; + +/** + * @author zuihou + * @create 2018-09-06 10:10 + * @desc 文件类型数量、 + * @Version 1.0 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class FileStatisticsDO { + /** + * 文件类型 IMAGE、DOC等 + */ + private DataType dataType; + /** + * 时间类型 (按月、周、天?) + */ + private String dateType; + /** + * 文件数量 + */ + private Integer num; + /** + * 文件大小 + */ + private Long size; +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/AttachmentDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/AttachmentDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..08374c2cfb47287cf59068ee4d7e49c16cee1b4e --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/AttachmentDTO.java @@ -0,0 +1,43 @@ +package org.springblade.file.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.springblade.file.entity.Attachment; + +import java.io.Serializable; + +/** + *

+ * 实体类 + * 附件表 + *

+ * + * @author zuihou + * @since 2019-05-20 + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@ApiModel(value = "AttachmentDTO", description = "附件表") +public class AttachmentDTO extends Attachment implements Serializable { + + /** + * 在DTO中新增并自定义字段,需要覆盖验证的字段,请新建DTO。Entity中的验证规则可以自行修改,但下次生成代码时,记得同步代码!! + */ + private static final long serialVersionUID = 1L; + @ApiModelProperty(value = "文件下载地址 根据url下载") + private String downloadUrlByUrl; + @ApiModelProperty(value = "文件下载地址 根据文件id下载") + private String downloadUrlById; + @ApiModelProperty(value = "文件下载地址 根据业务id下载") + private String downloadUrlByBizId; + + public static AttachmentDTO build() { + return new AttachmentDTO(); + } +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/AttachmentRemoveDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/AttachmentRemoveDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..b77096e57873049d28868b17cc7f96b092913d70 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/AttachmentRemoveDTO.java @@ -0,0 +1,27 @@ +package org.springblade.file.dto; + + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 删除实体 + * + * @param + * @author zuihou + * @date 2019-05-12 18:49 + * @return + */ +@Data +@ApiModel(value = "AttachmentRemove", description = "附件删除") +public class AttachmentRemoveDTO implements Serializable { + + @ApiModelProperty(value = "业务类型") + private String bizType; + + @ApiModelProperty(value = "业务id") + private String bizId; +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/AttachmentResultDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/AttachmentResultDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..63ec0c7a801a93121731feaac43499428f7b1d1a --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/AttachmentResultDTO.java @@ -0,0 +1,28 @@ +package org.springblade.file.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springblade.file.entity.Attachment; + +import java.util.List; + +/** + * 附件返回实体 + * + * @author zuihou + * @date 2018/12/12 + */ +@Data +@ApiModel(value = "AttachmentResult", description = "附件列表") +public class AttachmentResultDTO { + @ApiModelProperty(value = "业务id") + private String bizId; + @ApiModelProperty(value = "业务类型") + private String bizType; + @ApiModelProperty(value = "附件列表") + private List list; +} + + + diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/BaseFolderDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/BaseFolderDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..70820f9cbeceba5198fc77b50b37ec6074161bb9 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/BaseFolderDTO.java @@ -0,0 +1,36 @@ +package org.springblade.file.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +/** + * 文件夹基础DTO + * + * @author zuihou + * @date 2019-04-29 + */ +@Data +public abstract class BaseFolderDTO { + + /** + * 名称 + */ + @ApiModelProperty(value = "名称") + @NotEmpty(message = "文件名称不能为空") + protected String submittedFileName; + + /** + * 父文件夹 + */ + @ApiModelProperty(value = "父文件夹id") + protected Long folderId; + + /** + * 排序 + */ + @ApiModelProperty(value = "排序") + protected Integer orderNum; + +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..1968b2ace9d4156c2d1a5e04764c288092396007 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileDTO.java @@ -0,0 +1,39 @@ +package org.springblade.file.dto; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.Accessors; +import org.springblade.file.entity.File; + +import java.io.Serializable; + +/** + *

+ * 实体类 + * 文件表 + *

+ * + * @author zuihou + * @since 2019-06-24 + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = false) +@ApiModel(value = "FileDTO", description = "文件表") +public class FileDTO extends File implements Serializable { + + /** + * 在DTO中新增并自定义字段,需要覆盖验证的字段,请新建DTO。Entity中的验证规则可以自行修改,但下次生成代码时,记得同步代码!! + */ + private static final long serialVersionUID = 1L; + + public static FileDTO build() { + return new FileDTO(); + } + +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileOverviewDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileOverviewDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..682590071fb1c3b154b095015fab01a8aa60ad55 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileOverviewDTO.java @@ -0,0 +1,48 @@ +package org.springblade.file.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Data; + +/** + * 文件统计 概览DTO + * + * @author zuihou + * @date 2019/05/08 + */ +@Data +@ApiModel(value = "FileOverview", description = "云盘首页概览") +@Builder +public class FileOverviewDTO { + + @ApiModelProperty(value = "所有文件数量") + private Integer allFileNum; + + @ApiModelProperty(value = "所有文件大小") + private Long allFileSize; + + @ApiModelProperty(value = "文件夹数量") + private Integer dirNum; + + @ApiModelProperty(value = "图片文件数量") + private Integer imgNum; + + @ApiModelProperty(value = "文档文件数量") + private Integer docNum; + + @ApiModelProperty(value = "视频文件数量") + private Integer videoNum; + + @ApiModelProperty(value = "视音频文件数量") + private Integer audioNum; + + @ApiModelProperty(value = "其他文件数量") + private Integer otherNum; + + public static FileOverviewDTOBuilder myBuilder() { + FileOverviewDTOBuilder builder = FileOverviewDTO.builder(); + return builder.allFileSize(0L).allFileNum(0).dirNum(0).imgNum(0).docNum(0).videoNum(0). + audioNum(0).otherNum(0); + } +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FilePageReqDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FilePageReqDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..83bf32b50616de4f0c169bfacec1b491f4e71b67 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FilePageReqDTO.java @@ -0,0 +1,30 @@ +package org.springblade.file.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springblade.file.enumeration.DataType; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 文件分页列表请求参数 + * + * @author zuihou + * @date 2019-04-29 + */ +@Data +@ApiModel(value = "FilePageReq", description = "文件分页列表请求参数") +public class FilePageReqDTO implements Serializable { + @ApiModelProperty(value = "文件夹id") + private Long folderId; + @ApiModelProperty(value = "原始文件名") + private String submittedFileName; + @ApiModelProperty(value = "数据类型 null和''表示查询全部 图片:IMAGE 视频:VIDEO 音频:AUDIO 文档DOC 其他:OTHER", example = "IMAGE,VIDEO,AUDIO,DOC,OTHER") + private DataType dataType; + @ApiModelProperty(value = "开始时间") + private LocalDateTime startCreateTime; + @ApiModelProperty(value = "结束时间") + private LocalDateTime endCreateTime; +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileStatisticsAllDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileStatisticsAllDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..b19ff5602d1125edfe8626201368d58ff50fc1ea --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileStatisticsAllDTO.java @@ -0,0 +1,39 @@ +package org.springblade.file.dto; + +import io.swagger.annotations.ApiModel; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +/** + * 文件 按时间线统计数量和大小的 DTO + * + * @author zuihou + * @date 2019/05/08 + */ +@Data +@Builder +@ApiModel(value = "FileStatisticsAll", description = "统计所有类型文件的数量和大小") +public class FileStatisticsAllDTO { + private List dateList; + private List sizeList; + private List numList; + + private List dirNumList; + + private List imgSizeList; + private List imgNumList; + + private List videoSizeList; + private List videoNumList; + + private List audioSizeList; + private List audioNumList; + + private List docSizeList; + private List docNumList; + + private List otherSizeList; + private List otherNumList; +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileUpdateDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileUpdateDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..2bb3e8e76e174db64853ddca47f7662818b3c96c --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FileUpdateDTO.java @@ -0,0 +1,30 @@ +package org.springblade.file.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * 文件修改 + * + * @author zuihou + * @date 2019-05-06 + */ +@Data +@ApiModel(value = "FileUpdate", description = "文件修改") +public class FileUpdateDTO implements Serializable { + @ApiModelProperty(value = "id", required = true) + @NotNull(message = "文件id不能为空") + private Long id; + /** + * 原始文件名 + * + * @mbggenerated + */ + @ApiModelProperty(value = "文件原始名称") + private String submittedFileName; + +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FolderDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FolderDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..435422708228a4947ecf76200d618d8eb22eb95e --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FolderDTO.java @@ -0,0 +1,20 @@ +package org.springblade.file.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 文件夹 + * + * @author zuihou + * @date 2019-04-29 + */ +@Data +@ApiModel(value = "Folder", description = "文件夹") +public class FolderDTO extends BaseFolderDTO implements Serializable { + @ApiModelProperty(value = "id", notes = "文件夹id", required = true) + private Long id; +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FolderSaveDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FolderSaveDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..a6e63efb09f8bab2c82d7e2907e7e12e0614e737 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/FolderSaveDTO.java @@ -0,0 +1,17 @@ +package org.springblade.file.dto; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; + +/** + * 文件夹保存 + * + * @author zuihou + * @date 2019-04-29 + */ +@Data +@ApiModel(value = "FolderSave", description = "文件夹保存") +public class FolderSaveDTO extends BaseFolderDTO implements Serializable { +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/chunk/FileChunkCheckDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/chunk/FileChunkCheckDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..23e57d36e0eaadcbe3c8752684cc0488da60bf58 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/chunk/FileChunkCheckDTO.java @@ -0,0 +1,25 @@ +package org.springblade.file.dto.chunk; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; + +/** + * 分片检测参数 + * + * @author zuihou + * @date 2018/08/28 + */ +@Data +@ToString +@ApiModel(value = "FileChunkCheck", description = "文件分片信息") +public class FileChunkCheckDTO { + + @ApiModelProperty(value = "文件大小") + private Long size; + @ApiModelProperty(value = "文件唯一名") + private String name; + @ApiModelProperty(value = "分片索引") + private Integer chunkIndex; +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/chunk/FileChunksMergeDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/chunk/FileChunksMergeDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..a8b7d5a79212a6b4dca3ad23da1d57d5e1d2580b --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/chunk/FileChunksMergeDTO.java @@ -0,0 +1,38 @@ +package org.springblade.file.dto.chunk; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; + +/** + * 封建分片合并DTO + * + * @author zuihou + * @date 2018/08/28 + */ +@Data +@ToString +@ApiModel(value = "FileChunksMerge", description = "文件合并实体") +public class FileChunksMergeDTO { + + @ApiModelProperty(value = "文件唯一名 md5.js 生成的, 与后端生成的一致") + private String name; + @ApiModelProperty(value = "原始文件名") + private String submittedFileName; + + @ApiModelProperty(value = "md5", notes = "webuploader 自带的md5算法值, 与后端生成的不一致") + private String md5; + + @ApiModelProperty(value = "分片总数") + private Integer chunks; + @ApiModelProperty(value = "后缀") + private String ext; + @ApiModelProperty(value = "文件夹id") + private Long folderId; + + @ApiModelProperty(value = "大小") + private Long size; + @ApiModelProperty(value = "类型") + private String contextType; +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/chunk/FileUploadDTO.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/chunk/FileUploadDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..66311459d57a0717264ef351bd5a18233b5aeb30 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/dto/chunk/FileUploadDTO.java @@ -0,0 +1,36 @@ +package org.springblade.file.dto.chunk; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; + +/** + * 文件分片上传实体 + * + * @author zuihou + * @date 2018/08/29 + */ +@Data +@ApiModel(value = "FileUpload", description = "文件分片上传实体") +@ToString +public class FileUploadDTO { + @ApiModelProperty(value = "md5", notes = "webuploader 自带的md5算法值, 与后端生成的不一致") + private String md5; + @ApiModelProperty(value = "大小") + private Long size; + @ApiModelProperty(value = "文件唯一名 md5.js生成的, 与后端生成的一致") + private String name; + @ApiModelProperty(value = "分片总数") + private Integer chunks; + @ApiModelProperty(value = "当前分片") + private Integer chunk; + @ApiModelProperty(value = "最后更新时间") + private String lastModifiedDate; + @ApiModelProperty(value = "类型") + private String type; + @ApiModelProperty(value = "后缀") + private String ext; + @ApiModelProperty(value = "文件夹id") + private Long folderId; +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/entity/Attachment.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/entity/Attachment.java new file mode 100644 index 0000000000000000000000000000000000000000..08d340eacdb306ee20e10f5d33d493bedc7f15cc --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/entity/Attachment.java @@ -0,0 +1,233 @@ +package org.springblade.file.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; +import lombok.experimental.Accessors; +import org.hibernate.validator.constraints.Length; +import org.springblade.core.mp.base.BaseEntity; +import org.springblade.file.enumeration.DataType; + +import java.time.LocalDateTime; +import java.util.Date; + +/** + *

+ * 实体类 + * 附件 + *

+ * + * @author zuihou + * @since 2019-06-24 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +@TableName("f_attachment") +@ApiModel(value = "Attachment", description = "附件") +public class Attachment extends BaseEntity { + + private static final long serialVersionUID = 1L; + /** + * 主键id + */ + @ApiModelProperty(value = "主键") + @TableId(value = "id", type = IdType.ASSIGN_ID) + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + /** + * 业务ID + */ + @ApiModelProperty(value = "业务ID") + @Length(max = 64, message = "业务ID长度不能超过64") + @TableField("biz_id") + private String bizId; + + /** + * 业务类型 + * #AttachmentType + */ + @ApiModelProperty(value = "业务类型") + @Length(max = 255, message = "业务类型长度不能超过255") + @TableField("biz_type") + private String bizType; + + /** + * 数据类型 + * #DataType{DIR:目录;IMAGE:图片;VIDEO:视频;AUDIO:音频;DOC:文档;OTHER:其他} + */ + @ApiModelProperty(value = "数据类型") + @TableField("data_type") + private DataType dataType; + + /** + * 原始文件名 + */ + @ApiModelProperty(value = "原始文件名") + @Length(max = 255, message = "原始文件名长度不能超过255") + @TableField("submitted_file_name") + private String submittedFileName; + + /** + * FastDFS返回的组 + * 用于FastDFS + */ + @ApiModelProperty(value = "FastDFS返回的组") + @Length(max = 255, message = "FastDFS返回的组长度不能超过255") + @TableField("group_") + private String group; + + /** + * FastDFS的远程文件名 + * 用于FastDFS + */ + @ApiModelProperty(value = "FastDFS的远程文件名") + @Length(max = 255, message = "FastDFS的远程文件名长度不能超过255") + @TableField("path") + private String path; + + /** + * 文件相对路径 + */ + @ApiModelProperty(value = "文件相对路径") + @Length(max = 255, message = "文件相对路径长度不能超过255") + @TableField("relative_path") + private String relativePath; + + /** + * 文件访问链接 + * 需要通过nginx配置路由,才能访问 + */ + @ApiModelProperty(value = "文件访问链接") + @Length(max = 255, message = "文件访问链接长度不能超过255") + @TableField("url") + private String url; + + /** + * 文件md5值 + */ + @ApiModelProperty(value = "文件md5值") + @Length(max = 255, message = "文件md5值长度不能超过255") + @TableField("file_md5") + private String fileMd5; + + /** + * 文件上传类型 + * 取上传文件的值 + */ + @ApiModelProperty(value = "文件上传类型") + @Length(max = 255, message = "文件上传类型长度不能超过255") + @TableField("context_type") + private String contextType; + + /** + * 唯一文件名 + */ + @ApiModelProperty(value = "唯一文件名") + @Length(max = 255, message = "唯一文件名长度不能超过255") + @TableField("filename") + private String filename; + + /** + * 后缀 + * (没有.) + */ + @ApiModelProperty(value = "后缀") + @Length(max = 64, message = "后缀长度不能超过64") + @TableField("ext") + private String ext; + + /** + * 大小 + */ + @ApiModelProperty(value = "大小") + @TableField("size") + private Long size; + /** + * 组织ID + */ + @ApiModelProperty(value = "组织ID") + @TableField("org_id") + private Long orgId; + + /** + * 图标 + */ + @ApiModelProperty(value = "图标") + @Length(max = 64, message = "图标长度不能超过64") + @TableField("icon") + private String icon; + + /** + * 创建年月 + * 格式:yyyy-MM 用于统计 + */ + @ApiModelProperty(value = "创建年月") + @Length(max = 10, message = "创建年月长度不能超过10") + @TableField("create_month") + private String createMonth; + + /** + * 创建时处于当年的第几周 + * yyyy-ww 用于统计 + */ + @ApiModelProperty(value = "创建时处于当年的第几周") + @Length(max = 10, message = "创建时处于当年的第几周长度不能超过10") + @TableField("create_week") + private String createWeek; + + /** + * 创建年月日 + * 格式: yyyy-MM-dd 用于统计 + */ + @ApiModelProperty(value = "创建年月日") + @Length(max = 12, message = "创建年月日长度不能超过12") + @TableField("create_day") + private String createDay; + + + @Builder + public Attachment(Long id, Date createTime, Long createUser, Date updateTime, Long updateUser, + String bizId, String bizType, DataType dataType, String submittedFileName, + String group, String path, String relativePath, String url, + String fileMd5, String contextType, String filename, String ext, Long size, Long orgId, String icon, + String createMonth, String createWeek, String createDay) { + this.id = id; + this.setCreateTime(createTime); + this.setCreateUser(createUser); + this.setUpdateTime(updateTime); + this.setUpdateUser(updateUser); +// this.createTime = createTime; +// this.createUser = createUser; +// this.updateTime = updateTime; +// this.updateUser = updateUser; + this.bizId = bizId; + this.bizType = bizType; + this.dataType = dataType; + this.submittedFileName = submittedFileName; + this.group = group; + this.path = path; + this.relativePath = relativePath; + this.url = url; + this.fileMd5 = fileMd5; + this.contextType = contextType; + this.filename = filename; + this.ext = ext; + this.size = size; + this.orgId = orgId; + this.icon = icon; + this.createMonth = createMonth; + this.createWeek = createWeek; + this.createDay = createDay; + } + +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/entity/File.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/entity/File.java new file mode 100644 index 0000000000000000000000000000000000000000..8a2e0c43ccf6f257e0d4f1f4098677769272eeec --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/entity/File.java @@ -0,0 +1,254 @@ +package org.springblade.file.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; +import lombok.experimental.Accessors; +import org.hibernate.validator.constraints.Length; +import org.springblade.core.mp.base.BaseEntity; +import org.springblade.file.enumeration.DataType; + +import java.time.LocalDateTime; +import java.util.Date; + +/** + *

+ * 实体类 + * 文件表 + *

+ * + * @author zuihou + * @since 2019-06-24 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +@TableName("f_file") +@ApiModel(value = "File", description = "文件表") +public class File extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @ApiModelProperty(value = "主键") + @TableId(value = "id", type = IdType.ASSIGN_ID) + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + /** + * 数据类型 + * #DataType{DIR:目录;IMAGE:图片;VIDEO:视频;AUDIO:音频;DOC:文档;OTHER:其他} + */ + @ApiModelProperty(value = "数据类型") + @TableField("data_type") + private DataType dataType; + + /** + * 原始文件名 + */ + @ApiModelProperty(value = "原始文件名") + @Length(max = 255, message = "原始文件名长度不能超过255") + @TableField("submitted_file_name") + private String submittedFileName; + + /** + * 父目录层级关系 + */ + @ApiModelProperty(value = "父目录层级关系") + @Length(max = 255, message = "父目录层级关系长度不能超过255") + @TableField("tree_path") + private String treePath; + + /** + * 层级等级 + * 从1开始计算 + */ + @ApiModelProperty(value = "层级等级") + @TableField("grade") + private Integer grade; + + /** + * 是否删除 + * #BooleanStatus{TRUE:1,已删除;FALSE:0,未删除} + */ + @ApiModelProperty(value = "是否删除") + @TableField("is_delete") + private Boolean isDelete; + + /** + * 父文件夹ID + */ + @ApiModelProperty(value = "父文件夹ID") + @TableField("folder_id") + private Long folderId; + + /** + * 文件访问链接 + * 需要通过nginx配置路由,才能访问 + */ + @ApiModelProperty(value = "文件访问链接") + @Length(max = 255, message = "文件访问链接长度不能超过255") + @TableField("url") + private String url; + + /** + * 文件大小 + * 单位字节 + */ + @ApiModelProperty(value = "文件大小") + @TableField("size") + private Long size; + + /** + * 父文件夹名称 + */ + @ApiModelProperty(value = "父文件夹名称") + @Length(max = 255, message = "父文件夹名称长度不能超过255") + @TableField("folder_name") + private String folderName; + + /** + * FastDFS组 + * 用于FastDFS + */ + @ApiModelProperty(value = "FastDFS组") + @Length(max = 255, message = "FastDFS组长度不能超过255") + @TableField("group_") + private String group; + + /** + * FastDFS远程文件名 + * 用于FastDFS + */ + @ApiModelProperty(value = "FastDFS远程文件名") + @Length(max = 255, message = "FastDFS远程文件名长度不能超过255") + @TableField("path") + private String path; + + /** + * 文件的相对路径 + */ + @ApiModelProperty(value = "文件的相对路径 ") + @Length(max = 255, message = "文件的相对路径 长度不能超过255") + @TableField("relative_path") + private String relativePath; + + /** + * md5值 + */ + @ApiModelProperty(value = "md5值") + @Length(max = 255, message = "md5值长度不能超过255") + @TableField("file_md5") + private String fileMd5; + + /** + * 文件类型 + * 取上传文件的值 + */ + @ApiModelProperty(value = "文件类型") + @Length(max = 255, message = "文件类型长度不能超过255") + @TableField("context_type") + private String contextType; + + /** + * 唯一文件名 + */ + @ApiModelProperty(value = "唯一文件名") + @Length(max = 255, message = "唯一文件名长度不能超过255") + @TableField("filename") + private String filename; + + /** + * 文件名后缀 + * (没有.) + */ + @ApiModelProperty(value = "文件名后缀") + @Length(max = 64, message = "文件名后缀长度不能超过64") + @TableField("ext") + private String ext; + + /** + * 文件图标 + * 用于云盘显示 + */ + @ApiModelProperty(value = "文件图标") + @Length(max = 64, message = "文件图标长度不能超过64") + @TableField("icon") + private String icon; + + /** + * 创建时年月 + * 格式:yyyy-MM 用于统计 + */ + @ApiModelProperty(value = "创建时年月") + @Length(max = 10, message = "创建时年月长度不能超过10") + @TableField("create_month") + private String createMonth; + + /** + * 创建时年周 + * yyyy-ww 用于统计 + */ + @ApiModelProperty(value = "创建时年周") + @Length(max = 10, message = "创建时年周长度不能超过10") + @TableField("create_week") + private String createWeek; + + /** + * 创建时年月日 + * 格式: yyyy-MM-dd 用于统计 + */ + @ApiModelProperty(value = "创建时年月日") + @Length(max = 12, message = "创建时年月日长度不能超过12") + @TableField("create_day") + private String createDay; + + + @Builder + public File(Long id, Date createTime, Long createUser, Date updateTime, Long updateUser, + DataType dataType, String submittedFileName, String treePath, Integer grade, Boolean isDelete, + Long folderId, String url, Long size, String folderName, String group, String path, + String relativePath, String fileMd5, String contextType, String filename, String ext, String icon, + String createMonth, String createWeek, String createDay) { + this.id = id; + this.setCreateTime(createTime); + this.setCreateUser(createUser); + this.setUpdateTime(updateTime); + this.setUpdateUser(updateUser); +// this.createTime = createTime; +// this.createUser = createUser; +// this.updateTime = updateTime; +// this.updateUser = updateUser; + this.dataType = dataType; + this.submittedFileName = submittedFileName; + this.treePath = treePath; + this.grade = grade; + this.isDelete = isDelete; + this.folderId = folderId; + this.url = url; + this.size = size; + this.folderName = folderName; + this.group = group; + this.path = path; + this.relativePath = relativePath; + this.fileMd5 = fileMd5; + this.contextType = contextType; + this.filename = filename; + this.ext = ext; + this.icon = icon; + this.createMonth = createMonth; + this.createWeek = createWeek; + this.createDay = createDay; + } + +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/enumeration/DataType.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/enumeration/DataType.java new file mode 100644 index 0000000000000000000000000000000000000000..a36f59f09d71b1dd53eb79c36e14cfc10e454d33 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/enumeration/DataType.java @@ -0,0 +1,74 @@ +package org.springblade.file.enumeration; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springblade.common.baseEnum.BaseEnum; + +import java.util.stream.Stream; + + +/** + *

+ * 实体注释中生成的类型枚举 + * 文件回收站 + *

+ * + * @author sp + * @date 2020-09-14 + */ +@Getter +@AllArgsConstructor +@ApiModel(value = "DataType", description = "数据类型-枚举") +public enum DataType implements BaseEnum { + + /** + * DIR="目录" + */ + DIR("目录"), + /** + * IMAGE="图片" + */ + IMAGE("图片"), + /** + * VIDEO="视频" + */ + VIDEO("视频"), + /** + * AUDIO="音频" + */ + AUDIO("音频"), + /** + * DOC="文档" + */ + DOC("文档"), + /** + * OTHER="其他" + */ + OTHER("其他"), + ; + + @ApiModelProperty(value = "描述") + private String desc; + + public static DataType match(String val, DataType def) { + return Stream.of(values()).parallel().filter((item) -> item.name().equalsIgnoreCase(val)).findAny().orElse(def); + } + + public static DataType get(String val) { + return match(val, null); + } + + + public boolean eq(DataType val) { + return val == null ? false : eq(val.name()); + } + + @ApiModelProperty(value = "编码", allowableValues = "DIR,IMAGE,VIDEO,AUDIO,DOC,OTHER", example = "DIR") + @Override + public String getCode() { + return this.name(); + } + +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/enumeration/FileStorageType.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/enumeration/FileStorageType.java new file mode 100644 index 0000000000000000000000000000000000000000..c777f914a6fcb03afbba71236c211a0eea8301a1 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/enumeration/FileStorageType.java @@ -0,0 +1,28 @@ +package org.springblade.file.enumeration; + +/** + * 文件 存储类型 枚举 + * + * @author zuihou + * @date 2019/05/06 + */ +public enum FileStorageType { + /** + * 本地 + */ + LOCAL, + /** + * FastDFS + */ + FAST_DFS, + ALI, + QINIU, + ; + + public boolean eq(FileStorageType type) { + for (FileStorageType t : FileStorageType.values()) { + return t.equals(type); + } + return false; + } +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/enumeration/IconType.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/enumeration/IconType.java new file mode 100644 index 0000000000000000000000000000000000000000..9312dab6d5e61fe6950f406dd4a6b1152e7c3c3d --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/enumeration/IconType.java @@ -0,0 +1,208 @@ +package org.springblade.file.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * 图标 常量 + * + * @author zuihou + * @date 2019-06-12 + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +public enum IconType { + /** + * 图标 + */ + DIR("application/x-director", "el-icon-folder"), + /** + * 图标 + */ + TXT("txt", "el-icon-document"), + /** + * 图标 + */ +// EXE("exe", "fa-exe-o"), +// PDF("pdf", "fa-file-pdf-o"), +// // PSD("psd", "fa-ps-o"), +// XLX("xls", "fa-file-excel-o"), +// /** +// * 图标 +// */ +// XLSX("xlsx", "fa-file-excel-o"), +// /** +// * 图标 +// */ +// WPS("wps", "fa-file-word-o"), +// /** +// * 图标 +// */ +// WPT("pwt", "fa-file-word-o"), +// /** +// * 图标 +// */ +// JS("js", "fa-file-code-o"), +// /** +// * 图标 +// */ +// JSP("js", "fa-file-code-o"), +// /** +// * 图标 +// */ +// PROPERTIES("properties", "fa-file-code-o"), + + /** + * 图标 + */ +// CSS("css", "fa-file-code-o"), +// SWF("swf", "fa-swf-o"), + /** + * 图标 + */ +// DOC("doc", "fa-file-word-o"), +// /** +// * 图标 +// */ +// DOCX("docx", "fa-file-word-o"), +// /** +// * 图标 +// */ +// DOTX("dotx", "fa-file-word-o"), +// /** +// * 图标 +// */ +// PPS("pps", "fa-file-powerpoint-o"), +// /** +// * 图标 +// */ +// PPT("ppt", "fa-file-powerpoint-o"), +// /** +// * 图标 +// */ +// POT("pot", "fa-file-powerpoint-o"), +// /** +// * 图标 +// */ +// PPTX("pptx", "fa-file-powerpoint-o"), +// HTML("html", "fa-html-o"), +// HTM("htm", "fa-html-o"), + +// JAR("jar", "fa-file-jar-o"), +// WAR("war", "fa-file-war-o"), + /** + * 图标 + */ +// GZIP("gzip", "fa-file-zip-o"), +// /** +// * 图标 +// */ +// TAR("tar", "fa-file-zip-o"), +// /** +// * 图标 +// */ +// ZIP("zip", "fa-file-zip-o"), +// /** +// * 图标 +// */ +// RAR("rar", "fa-file-zip-o"), +// /** +// * 图标 +// */ +// TAR_GZ("tar.gz", "fa-file-zip-o"), + + /** + * 图标 + */ + MP3("mp3", "el-icon-microphone"), + /** + * 图标 + */ + + BMP("bmp", "el-icon-picture"), + /** + * 图标 + */ + JPEG("jpeg", "el-icon-picture"), + /** + * 图标 + */ + JPG("jpg", "el-icon-picture"), + + /** + * 图标 + */ + PNG("png", "el-icon-picture"), + /** + * 图标 + */ + GIF("gif", "el-icon-picture"), + /** + * 图标 + */ + COD("cod", "el-icon-picture"), + /** + * 图标 + */ + IEF("ief", "el-icon-picture"), + /** + * 图标 + */ + SVG("svg", "el-icon-picture"), + /** + * 图标 + */ + ICO("ico", "el-icon-picture"), + /** + * 图标 + */ + ICON("icon", "el-icon-picture"), + /** + * 图标 + */ + AVI("avi", "el-icon-video-camera"), + /** + * 图标 + */ + MP4("mp4", "el-icon-video-camera"), + /** + * 图标 + */ + MOV("mov", "el-icon-video-camera"), + /** + * 图标 + */ + MPG("mpg", "el-icon-video-camera"), + /** + * 图标 + */ + WMV("wmv", "el-icon-video-camera"), + /** + * 图标 + */ + WAV("wav", "el-icon-video-camera"), + + BAT("wav", "el-icon-video-camera"), + /** + * 图标 + */ + OTHER("*", "el-icon-question"), + ; + + private String ext; + private String icon; + + public static IconType getIcon(String ext) { + if (ext == null || ext.isEmpty()) { + return OTHER; + } + for (IconType it : IconType.values()) { + if (it.getExt().equalsIgnoreCase(ext)) { + return it; + } + } + return OTHER; + } +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/feign/AttachmentClient.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/feign/AttachmentClient.java new file mode 100644 index 0000000000000000000000000000000000000000..e30c43c722641a3ac6bb1a6b357d1262f252a11c --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/feign/AttachmentClient.java @@ -0,0 +1,40 @@ +package org.springblade.file.feign; + + +import org.springblade.core.tool.api.R; +import org.springblade.core.launch.constant.AppConstant; +import org.springblade.file.dto.AttachmentDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件接口 + */ +@FeignClient( + value = "file-server",// AppConstant.APPLICATION_USER_NAME, + fallback = AttachmentClient.class +) +public interface AttachmentClient { + + /** + * 通过feign-form 实现文件 跨服务上传 + * + * @param file + * @param id + * @param bizId + * @param bizType + * @return + */ + @PostMapping(value = "/attachment/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + R upload( + @RequestPart(value = "file") MultipartFile file, + @RequestParam(value = "isSingle", required = false, defaultValue = "false") Boolean isSingle, + @RequestParam(value = "id", required = false) Long id, + @RequestParam(value = "bizId", required = false) String bizId, + @RequestParam(value = "bizType", required = false) String bizType); + +} diff --git a/blade-service-api/blade-file-api/src/main/java/org/springblade/file/feign/AttachmentClientFallback.java b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/feign/AttachmentClientFallback.java new file mode 100644 index 0000000000000000000000000000000000000000..256ffafa2dd19b3fc6f6401353f60ab1af87a018 --- /dev/null +++ b/blade-service-api/blade-file-api/src/main/java/org/springblade/file/feign/AttachmentClientFallback.java @@ -0,0 +1,20 @@ +package org.springblade.file.feign; + +import org.springblade.core.tool.api.R; +import org.springblade.file.dto.AttachmentDTO; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +/** + * 熔断 + * + * @author zt + * @date 2020/09/10 + */ +@Component +public class AttachmentClientFallback implements AttachmentClient { + @Override + public R upload(MultipartFile file, Boolean isSingle, Long id, String bizId, String bizType) { + return R.fail("上传文件失败!!!"); + } +} diff --git a/blade-service-api/blade-file-api/src/test/java/org/springblade/AppTest.java b/blade-service-api/blade-file-api/src/test/java/org/springblade/AppTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1359a54c38e922f7dddaf46462eb46cfcff2f131 --- /dev/null +++ b/blade-service-api/blade-file-api/src/test/java/org/springblade/AppTest.java @@ -0,0 +1,20 @@ +package org.springblade; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/blade-service-api/blade-stock-api/doc/README.md b/blade-service-api/blade-stock-api/doc/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0dbcc1e4632c73e699ec20a16e2591ff76842d2f --- /dev/null +++ b/blade-service-api/blade-stock-api/doc/README.md @@ -0,0 +1,8 @@ +选股总结: +个股关注模型 重点看图 : + 个股分析思路 : + 一般分析股票的思路4点 依次 + 第一 看趋势 判断空间 + 第二 看资金 判断强弱 +第三 买票 看支撑 (缺口 年线半年线 辅助线 5日线 平台支撑等等) +第四 卖票 看压力 (缺口 年线半年线 平台高点 M头 等等 ) \ No newline at end of file diff --git a/blade-service-api/blade-stock-api/doc/pic/sql/sqlDesign.jpg b/blade-service-api/blade-stock-api/doc/pic/sql/sqlDesign.jpg new file mode 100644 index 0000000000000000000000000000000000000000..24d9afba3d10d7547d0bdaffe44d57f2ab66fde7 Binary files /dev/null and b/blade-service-api/blade-stock-api/doc/pic/sql/sqlDesign.jpg differ diff --git "a/blade-service-api/blade-stock-api/doc/pic/sql/stock\350\241\250.eddx" "b/blade-service-api/blade-stock-api/doc/pic/sql/stock\350\241\250.eddx" new file mode 100644 index 0000000000000000000000000000000000000000..5e9a009850d8f24bb176890670546ed52db83162 Binary files /dev/null and "b/blade-service-api/blade-stock-api/doc/pic/sql/stock\350\241\250.eddx" differ diff --git "a/blade-service-api/blade-stock-api/doc/pic/\350\241\214\346\203\205\344\273\223\344\275\215\346\257\224\344\276\213.jpg" "b/blade-service-api/blade-stock-api/doc/pic/\350\241\214\346\203\205\344\273\223\344\275\215\346\257\224\344\276\213.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..328339be8f8d7b20af8e41396f66deac9ad3642f Binary files /dev/null and "b/blade-service-api/blade-stock-api/doc/pic/\350\241\214\346\203\205\344\273\223\344\275\215\346\257\224\344\276\213.jpg" differ diff --git a/blade-service-api/blade-stock-api/pom.xml b/blade-service-api/blade-stock-api/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..2f4d743218b2a7876fac5d8f1267418320bd0ca5 --- /dev/null +++ b/blade-service-api/blade-stock-api/pom.xml @@ -0,0 +1,17 @@ + + + + + blade-service-api + org.springblade + 2.7.1 + + 4.0.0 + blade-stock-api + ${project.artifactId} + ${blade.project.version} + jar + + + diff --git a/blade-service-api/pom.xml b/blade-service-api/pom.xml index e4feba06a58da04354f57a063915e2446d95cf52..fa397593ac5ffe52f94b775647ed872cb2fce6fd 100644 --- a/blade-service-api/pom.xml +++ b/blade-service-api/pom.xml @@ -21,6 +21,8 @@ blade-system-api blade-user-api blade-demo-api + blade-stock-api + blade-file-api diff --git a/blade-service/blade-desk/src/main/resources/application-dev.yml b/blade-service/blade-desk/src/main/resources/application-dev.yml index 5596f7846d847743f27e2c1fae1bcf55587d0aa1..77fcf659ac89d36996440387a444a32e05d3a5ff 100644 --- a/blade-service/blade-desk/src/main/resources/application-dev.yml +++ b/blade-service/blade-desk/src/main/resources/application-dev.yml @@ -7,4 +7,4 @@ spring: datasource: url: ${blade.datasource.dev.url} username: ${blade.datasource.dev.username} - password: ${blade.datasource.dev.password} \ No newline at end of file + password: ${blade.datasource.dev.password} diff --git a/blade-service/blade-file/Dockerfile b/blade-service/blade-file/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..ec6b85320bebd9b659ce15e515d769a15cbeb081 --- /dev/null +++ b/blade-service/blade-file/Dockerfile @@ -0,0 +1,15 @@ +FROM anapsix/alpine-java:8_server-jre_unlimited + +MAINTAINER w1999wtw3537@sina.com + +RUN mkdir -p /blade/file + +WORKDIR /blade/file + +EXPOSE 8203 + +ADD ./target/blade-file.jar ./app.jar + +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] + +CMD ["--spring.profiles.active=test"] diff --git a/blade-service/blade-file/pom.xml b/blade-service/blade-file/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..2d20e539884dad9ca9e242a5bda7fe25ee7b248b --- /dev/null +++ b/blade-service/blade-file/pom.xml @@ -0,0 +1,78 @@ + + + + + blade-service + org.springblade + 2.7.1 + + + 4.0.0 + + blade-file + ${project.artifactId} + ${blade.project.version} + jar + + + + org.springblade + blade-core-boot + ${blade.tool.version} + + + org.springblade + blade-file-api + ${blade.project.version} + + + + com.aliyun.oss + aliyun-sdk-oss + 3.1.0 + + + + com.qiniu + qiniu-java-sdk + 7.2.18 + + + + com.github.tobato + fastdfs-client + 1.27.2 + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + package + + run + + + + + + + + + + + + + diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/FileServerApplication.java b/blade-service/blade-file/src/main/java/org/springblade/file/FileServerApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..d229639380c8a4b9e43679b5e4896f9c35dad882 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/FileServerApplication.java @@ -0,0 +1,47 @@ +package com.github.zuihou; + +import com.github.zuihou.security.annotation.EnableLoginArgResolver; +import com.github.zuihou.validator.annotation.EnableFormValidator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.netflix.hystrix.EnableHystrix; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * @author zuihou + */ +@SpringBootApplication +@EnableDiscoveryClient +@EnableHystrix +@EnableFeignClients(value = { + "com.github.zuihou", +}) +@EnableTransactionManagement +@Slf4j +@EnableLoginArgResolver +@EnableFormValidator +public class FileServerApplication { + public static void main(String[] args) throws UnknownHostException { + ConfigurableApplicationContext application = SpringApplication.run(FileServerApplication.class, args); + Environment env = application.getEnvironment(); + log.info("\n----------------------------------------------------------\n\t" + + "应用 '{}' 运行成功! 访问连接:\n\t" + + "Swagger文档: \t\thttp://{}:{}/doc.html\n\t" + + "数据库监控: \t\thttp://{}:{}/druid\n" + + "----------------------------------------------------------", + env.getProperty("spring.application.name"), + InetAddress.getLocalHost().getHostAddress(), + env.getProperty("server.port"), + "127.0.0.1", + env.getProperty("server.port")); + + } +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/authScope/DataScope.java b/blade-service/blade-file/src/main/java/org/springblade/file/authScope/DataScope.java new file mode 100644 index 0000000000000000000000000000000000000000..f66398c7ac5a085bad35c7564d9da76707b54913 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/authScope/DataScope.java @@ -0,0 +1,135 @@ +package org.springblade.file.authScope; + +import java.util.HashMap; +import java.util.List; + +public class DataScope extends HashMap { + private String scopeName = "org_id"; + private String selfScopeName = "create_user"; + private Long userId; + private List orgIds; + + public DataScope() { + } + + public String getScopeName() { + return this.scopeName; + } + + public String getSelfScopeName() { + return this.selfScopeName; + } + + public Long getUserId() { + return this.userId; + } + + public List getOrgIds() { + return this.orgIds; + } + + public void setScopeName(String scopeName) { + this.scopeName = scopeName; + } + + public void setSelfScopeName(String selfScopeName) { + this.selfScopeName = selfScopeName; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public void setOrgIds(List orgIds) { + this.orgIds = orgIds; + } + + @Override + public String toString() { + return "DataScope(scopeName=" + this.getScopeName() + ", selfScopeName=" + this.getSelfScopeName() + ", userId=" + this.getUserId() + ", orgIds=" + this.getOrgIds() + ")"; + } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (!(o instanceof DataScope)) { + return false; + } else { + DataScope other = (DataScope)o; + if (!other.canEqual(this)) { + return false; + } else if (!super.equals(o)) { + return false; + } else { + label61: { + Object this$scopeName = this.getScopeName(); + Object other$scopeName = other.getScopeName(); + if (this$scopeName == null) { + if (other$scopeName == null) { + break label61; + } + } else if (this$scopeName.equals(other$scopeName)) { + break label61; + } + + return false; + } + + label54: { + Object this$selfScopeName = this.getSelfScopeName(); + Object other$selfScopeName = other.getSelfScopeName(); + if (this$selfScopeName == null) { + if (other$selfScopeName == null) { + break label54; + } + } else if (this$selfScopeName.equals(other$selfScopeName)) { + break label54; + } + + return false; + } + + Object this$userId = this.getUserId(); + Object other$userId = other.getUserId(); + if (this$userId == null) { + if (other$userId != null) { + return false; + } + } else if (!this$userId.equals(other$userId)) { + return false; + } + + Object this$orgIds = this.getOrgIds(); + Object other$orgIds = other.getOrgIds(); + if (this$orgIds == null) { + if (other$orgIds != null) { + return false; + } + } else if (!this$orgIds.equals(other$orgIds)) { + return false; + } + + return true; + } + } + } + + protected boolean canEqual(Object other) { + return other instanceof DataScope; + } + @Override + public int hashCode() { +// int PRIME = true; + boolean PRIME = true; + int result = super.hashCode(); + Object $scopeName = this.getScopeName(); + result = result * 59 + ($scopeName == null ? 43 : $scopeName.hashCode()); + Object $selfScopeName = this.getSelfScopeName(); + result = result * 59 + ($selfScopeName == null ? 43 : $selfScopeName.hashCode()); + Object $userId = this.getUserId(); + result = result * 59 + ($userId == null ? 43 : $userId.hashCode()); + Object $orgIds = this.getOrgIds(); + result = result * 59 + ($orgIds == null ? 43 : $orgIds.hashCode()); + return result; + } +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/biz/FileBiz.java b/blade-service/blade-file/src/main/java/org/springblade/file/biz/FileBiz.java new file mode 100644 index 0000000000000000000000000000000000000000..fd997ef5e79def7c89ef0b7e2d2c34a9d342065b --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/biz/FileBiz.java @@ -0,0 +1,85 @@ +package org.springblade.file.biz; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import com.github.zuihou.file.utils.ZipUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springblade.file.domain.FileDO; +import org.springblade.file.enumeration.DataType; +import org.springblade.file.properties.FileServerProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 文件和附件的一些公共方法 + * + * @author zuihou + * @date 2019/05/06 + */ +@Component +@Slf4j +public class FileBiz { + + @Autowired + private FileServerProperties fileProperties; + + private static String buildNewFileName(String filename, Integer order) { + return StrUtil.strBuilder(filename).insert(filename.lastIndexOf("."), "(" + order + ")").toString(); + } + + public void down(List list, HttpServletRequest request, HttpServletResponse response) throws Exception { + //获取内网前缀地址 + String innerUriPrefix = fileProperties.getInnerUriPrefix(); + String remoteUriPrefix = fileProperties.getUriPrefix(); + log.info("内网前缀地址 innerUriPrefix={}", innerUriPrefix); + long fileSize = list.stream().filter((file) -> file != null && !DataType.DIR.eq(file.getDataType()) && StringUtils.isNotEmpty(file.getUrl())) + .mapToLong((file) -> Convert.toLong(file.getSize(), 0L)).sum(); + String extName = list.get(0).getSubmittedFileName(); + if (list.size() > 1) { + extName = StringUtils.substring(extName, 0, StringUtils.lastIndexOf(extName, ".")) + "等.zip"; + } + + Map map = new LinkedHashMap<>(list.size()); + Map duplicateFile = new HashMap<>(list.size()); + list.stream() + //过滤不符合要求的文件 + .filter((file) -> file != null && !DataType.DIR.eq(file.getDataType()) && StringUtils.isNotEmpty(file.getUrl())) + //将外网地址转成内网地址 + .map((file) -> { + String url = file.getUrl(); + if (StringUtils.isNotEmpty(innerUriPrefix)) { + //转为内网渠道下载 + url = url.replaceAll(remoteUriPrefix, innerUriPrefix); + log.info("文件转内网 url地址 ={}", url); + } + file.setUrl(url); + return file; + }) + //循环处理相同的文件名 + .forEach((file) -> { + String submittedFileName = file.getSubmittedFileName(); + if (map.containsKey(submittedFileName)) { + if (duplicateFile.containsKey(submittedFileName)) { + duplicateFile.put(submittedFileName, duplicateFile.get(submittedFileName) + 1); + } else { + duplicateFile.put(submittedFileName, 1); + } + submittedFileName = buildNewFileName(submittedFileName, duplicateFile.get(submittedFileName)); + } + map.put(submittedFileName, file.getUrl()); + }); + + + ZipUtils.zipFilesByInputStream(map, Long.valueOf(fileSize), extName, request, response); + } + + +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/controller/AttachmentController.java b/blade-service/blade-file/src/main/java/org/springblade/file/controller/AttachmentController.java new file mode 100644 index 0000000000000000000000000000000000000000..d6df252cdce4ca509329157ed8ce31dece690d02 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/controller/AttachmentController.java @@ -0,0 +1,213 @@ +package org.springblade.file.controller; + + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.springblade.core.tool.api.R; +import org.springblade.file.dto.AttachmentDTO; +import org.springblade.file.dto.FilePageReqDTO; +import org.springblade.file.entity.Attachment; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.groupingBy; + +/** + *

+ * 附件表 前端控制器 + *

+ * + * @author zuihou + * @since 2019-04-29 + */ +@RestController +@RequestMapping("/attachment") +@Slf4j +@Api(value = "附件", tags = "附件") +@Validated +public class AttachmentController{ + + /** + * 业务类型判断符 + */ + private final static String TYPE_BIZ_ID = "bizId"; + + @Override + public void query(PageParams params, IPage page, Long defSize) { + baseService.page(page, params.getModel()); + } + + @Override + public R handlerDelete(List ids) { + return R.success(baseService.remove(ids)); + } + + /** + * 上传文件 + * + * @param + * @return + * @author zuihou + * @date 2019-05-06 16:28 + */ + @ApiOperation(value = "附件上传", notes = "附件上传") + @ApiImplicitParams({ + @ApiImplicitParam(name = "isSingle", value = "是否单文件", dataType = "boolean", paramType = "query"), + @ApiImplicitParam(name = "id", value = "文件id", dataType = "long", paramType = "query"), + @ApiImplicitParam(name = "bizId", value = "业务id", dataType = "long", paramType = "query"), + @ApiImplicitParam(name = "bizType", value = "业务类型", dataType = "long", paramType = "query"), + @ApiImplicitParam(name = "file", value = "附件", dataType = "MultipartFile", allowMultiple = true, required = true), + }) + @PostMapping(value = "/upload") + public R upload( + @RequestParam(value = "file") MultipartFile file, + @RequestParam(value = "isSingle", required = false, defaultValue = "false") Boolean isSingle, + @RequestParam(value = "id", required = false) Long id, + @RequestParam(value = "bizId", required = false) String bizId, + @RequestParam(value = "bizType", required = false) String bizType) { + + if(StringUtils.isBlank(bizType)){ + return R.fail("业务类型不能为空"); + } + // 忽略路径字段,只处理文件类型 + if (file.isEmpty()) { + return R.fail("请求中必须至少包含一个有效文件"); + } + String tenant = BaseContextHandler.getTenant(); + + AttachmentDTO attachment = baseService.upload(file, tenant, id, bizType, bizId, isSingle); + return R.data(attachment); + } + + + @ApiOperation(value = "根据业务类型或业务id删除文件", notes = "根据业务类型或业务id删除文件") + @DeleteMapping(value = "/biz") + @SysLog("根据业务类型删除附件") + public R removeByBizIdAndBizType(@RequestBody AttachmentRemoveDTO dto) { + return R.success(baseService.removeByBizIdAndBizType(dto.getBizId(), dto.getBizType())); + } + + @ApiOperation(value = "查询附件", notes = "查询附件") + @ApiResponses( + @ApiResponse(code = 60103, message = "文件id为空") + ) + @GetMapping + @SysLog("根据业务类型查询附件") + public R> findAttachment(@RequestParam(value = "bizTypes", required = false) String[] bizTypes, + @RequestParam(value = "bizIds", required = false) String[] bizIds) { + //不能同时为空 + BizAssert.isTrue(!(ArrayUtils.isEmpty(bizTypes) && ArrayUtils.isEmpty(bizIds)), BASE_VALID_PARAM.build("业务类型不能为空")); + return R.success(baseService.find(bizTypes, bizIds)); + } + + @ApiOperation(value = "根据业务类型或者业务id查询附件", notes = "根据业务类型或者业务id查询附件") + @GetMapping(value = "/biz/{type}") + @SysLog("根据业务类型分组查询附件") + public R>> findAttachmentByBiz(@PathVariable String type, @RequestParam("biz[]") String[] biz) { + SFunction sf = Attachment::getBizType; + if (TYPE_BIZ_ID.equalsIgnoreCase(type)) { + sf = Attachment::getBizId; + } + List list = baseService.list(Wrappers.lambdaQuery().in(sf, biz).orderByAsc(Attachment::getCreateTime)); + if (list.isEmpty()) { + return R.success(MapUtils.EMPTY_MAP); + } + if (TYPE_BIZ_ID.equalsIgnoreCase(type)) { + return R.success(list.stream().collect(groupingBy(Attachment::getBizType))); + } else { + return R.success(list.stream().collect(groupingBy(Attachment::getBizId))); + } + } + + + /** + * 下载一个文件或多个文件打包下载 + * + * @param ids 文件id + * @param response + * @throws Exception + */ + @ApiOperation(value = "根据文件id打包下载", notes = "根据附件id下载多个打包的附件") + @GetMapping(value = "/download", produces = "application/octet-stream") + @SysLog("下载附件") + public void download( + @ApiParam(name = "ids[]", value = "文件id 数组") + @RequestParam(value = "ids[]") Long[] ids, + HttpServletRequest request, HttpServletResponse response) throws Exception { + BizAssert.isTrue(ArrayUtils.isNotEmpty(ids), BASE_VALID_PARAM.build("附件id不能为空")); + baseService.download(request, response, ids); + } + + /** + * 根据业务类型或者业务id其中之一,或者2个同时打包下载文件 + * + * @param bizIds 业务id + * @param bizTypes 业务类型 + * @return + * @author zuihou + * @date 2019-05-12 21:23 + */ + @ApiImplicitParams({ + @ApiImplicitParam(name = "bizIds[]", value = "业务id数组", dataType = "array", paramType = "query"), + @ApiImplicitParam(name = "bizTypes[]", value = "业务类型数组", dataType = "array", paramType = "query"), + }) + @ApiOperation(value = "根据业务类型/业务id打包下载", notes = "根据业务id下载一个文件或多个文件打包下载") + @GetMapping(value = "/download/biz", produces = "application/octet-stream") + @SysLog("根据业务类型下载附件") + public void downloadByBiz( + @RequestParam(value = "bizIds[]", required = false) String[] bizIds, + @RequestParam(value = "bizTypes[]", required = false) String[] bizTypes, + HttpServletRequest request, HttpServletResponse response) throws Exception { + BizAssert.isTrue(!(ArrayUtils.isEmpty(bizTypes) && ArrayUtils.isEmpty(bizIds)), BASE_VALID_PARAM.build("附件业务id和业务类型不能同时为空")); + baseService.downloadByBiz(request, response, bizTypes, bizIds); + } + + /** + * 根据下载地址下载文件 + * + * @param url 文件链接 + * @param filename 文件名称 + * @return + * @author zuihou + * @date 2019-05-12 21:24 + */ + @ApiOperation(value = "根据url下载文件(不推荐)", notes = "根据文件的url下载文件(不推荐使用,若要根据url下载,请执行通过nginx)") + @ApiImplicitParams({ + @ApiImplicitParam(name = "url", value = "文件url", dataType = "string", paramType = "query"), + @ApiImplicitParam(name = "filename", value = "文件名", dataType = "string", paramType = "query"), + }) + @GetMapping(value = "/download/url", produces = "application/octet-stream") + @SysLog("根据文件连接下载文件") + public void downloadUrl(@RequestParam(value = "url") String url, @RequestParam(value = "filename", required = false) String filename, + HttpServletRequest request, HttpServletResponse response) throws Exception { + BizAssert.isTrue(StringUtils.isNotEmpty(url), BASE_VALID_PARAM.build("附件下载地址不能为空")); + log.info("name={}, url={}", filename, url); + baseService.downloadByUrl(request, response, url, filename); + } + +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/controller/FileChunkController.java b/blade-service/blade-file/src/main/java/org/springblade/file/controller/FileChunkController.java new file mode 100644 index 0000000000000000000000000000000000000000..9b7fb8ad3750d92f349ee04e3369493f8bdd6d58 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/controller/FileChunkController.java @@ -0,0 +1,151 @@ +package org.springblade.file.controller; + + +import com.github.zuihou.file.service.FileService; +import com.github.zuihou.file.strategy.FileChunkStrategy; +import com.github.zuihou.file.strategy.FileStrategy; +import com.github.zuihou.file.utils.FileDataTypeUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.api.R; +import org.springblade.file.domain.FileAttrDO; +import org.springblade.file.dto.chunk.FileChunkCheckDTO; +import org.springblade.file.dto.chunk.FileChunksMergeDTO; +import org.springblade.file.dto.chunk.FileUploadDTO; +import org.springblade.file.entity.File; +import org.springblade.file.manager.WebUploader; +import org.springblade.file.properties.FileServerProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.nio.file.Paths; + +/** + * 注意:该类下的所有方法均需要webuploder.js插件进行配合使用。 + * md5 + * + * @author zuihou + * @date 2018/08/24 + */ +@RestController +@Slf4j +@RequestMapping("/chunk") +@Api(value = "文件续传+秒传", tags = "文件续传+秒传功能,所有方法均需要webuploder.js插件进行配合使用, 且4个方法需要配合使用,单核接口没有意义") +public class FileChunkController { + @Autowired + private FileServerProperties fileProperties; + @Autowired + private FileService fileService; + @Resource + private FileStrategy fileStrategy; + @Resource + private FileChunkStrategy fileChunkStrategy; + @Autowired + private WebUploader wu; + + + /** + * 采用md5 上传前的验证 + * + * @param md5 文件md5 + * @return + */ + @ApiOperation(value = "秒传接口,上传文件前先验证, 存在则启动秒传", notes = "前端通过webUploader获取文件md5,上传前的验证") + @RequestMapping(value = "/md5", method = RequestMethod.POST) + @ResponseBody + public R saveMd5Check(@RequestParam(name = "md5") String md5, + @RequestParam(name = "folderId", defaultValue = "0") Long folderId) { + Long accountId = BaseContextHandler.getUserId(); + File file = fileChunkStrategy.md5Check(md5, folderId, accountId); + return R.success(file != null ? true : false); + } + + /** + * 检查分片存不存在 + * + * @param info + * @return + */ + @ApiOperation(value = "续传接口,检查每个分片存不存在", notes = "断点续传功能检查分片是否存在, 已存在的分片无需重复上传, 达到续传效果") + @RequestMapping(value = "/check", method = RequestMethod.POST) + @ResponseBody + public R chunkCheck(@RequestBody FileChunkCheckDTO info) { + log.info("info={}", info); + String uploadFolder = FileDataTypeUtil.getUploadPathPrefix(fileProperties.getStoragePath()); + //检查目标分片是否存在且完整 + boolean chunkCheck = wu.chunkCheck(Paths.get(uploadFolder, info.getName(), String.valueOf(info.getChunkIndex())).toString(), info.getSize()); + return R.success(chunkCheck); + } + + + /** + * 分片上传 + * 该接口不能用作 单文件上传! + * + * @param info + * @param file + * @return + */ + @ApiOperation(value = "分片上传", notes = "前端通过webUploader获取截取分片, 然后逐个上传") + @RequestMapping(value = "/upload", method = RequestMethod.POST) + @ResponseBody + public R uploadFile(FileUploadDTO info, @RequestParam(value = "file", required = false) MultipartFile file) throws Exception { + String uploadFolder = FileDataTypeUtil.getUploadPathPrefix(fileProperties.getStoragePath()); + //验证请求不会包含数据上传,所以避免NullPoint这里要检查一下file变量是否为null + if (file == null || file.isEmpty()) { + log.error("请求参数不完整"); + return R.fail("请求参数不完整"); + } + + log.info("info={}", info); + /* + 将MD5签名和合并后的文件path存入持久层,注意这里这个需求导致需要修改webuploader.js源码3170行 + 因为原始webuploader.js不支持为formData设置函数类型参数,这将导致不能在控件初始化后修改该参数 + 文件大小 小于 单个分片时,会执行这里的代码 + */ + if (info.getChunks() == null || info.getChunks() <= 0) { + File upload = fileStrategy.upload(file); + + FileAttrDO fileAttrDO = fileService.getFileAttrDo(info.getFolderId()); + upload.setFolderId(info.getFolderId()); + upload.setFileMd5(info.getMd5()); + upload.setFolderName(fileAttrDO.getFolderName()); + upload.setGrade(fileAttrDO.getGrade()); + upload.setTreePath(fileAttrDO.getTreePath()); + fileService.save(upload); + return R.success(file.getOriginalFilename()); + } else { + //为上传的文件准备好对应的位置 + java.io.File target = wu.getReadySpace(info, uploadFolder); + log.info("target={}", target.getAbsolutePath()); + if (target == null) { + return R.fail(wu.getErrorMsg()); + } + //保存上传文件 + file.transferTo(target); + return R.success(target.getName()); + } + + + } + + /** + * 分片通过nio合并, 合并成功后,将文件上传至fastdfs + * nio合并优点: 有效防止大文件的内存溢出 + * + * @param info + * @return + */ + @ApiOperation(value = "分片合并", notes = "所有分片上传成功后,调用该接口对分片进行合并") + @RequestMapping(value = "/merge", method = RequestMethod.POST) + @ResponseBody + public R saveChunksMerge(@RequestBody FileChunksMergeDTO info) { + log.info("info={}", info); + + return fileChunkStrategy.chunksMerge(info); + } + +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/controller/FileController.java b/blade-service/blade-file/src/main/java/org/springblade/file/controller/FileController.java new file mode 100644 index 0000000000000000000000000000000000000000..3cd9bba5b136de7bb70c54f4e54e6a3bd1d5b3e9 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/controller/FileController.java @@ -0,0 +1,130 @@ +package org.springblade.file.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.github.zuihou.base.R; +import com.github.zuihou.base.controller.SuperController; +import com.github.zuihou.base.request.PageParams; +import com.github.zuihou.file.dto.FilePageReqDTO; +import com.github.zuihou.file.dto.FileUpdateDTO; +import com.github.zuihou.file.dto.FolderDTO; +import com.github.zuihou.file.dto.FolderSaveDTO; +import com.github.zuihou.file.entity.File; +import com.github.zuihou.file.manager.FileRestManager; +import com.github.zuihou.file.service.FileService; +import com.github.zuihou.log.annotation.SysLog; +import com.github.zuihou.utils.BeanPlusUtil; +import io.swagger.annotations.*; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + *

+ * 文件表 前端控制器 + *

+ * + * @author zuihou + * @since 2019-04-29 + */ +@Validated +@RestController +@RequestMapping("/file") +@Slf4j +@Api(value = "文件表", tags = "文件表") +public class FileController extends SuperController { + @Autowired + private FileRestManager fileRestManager; + + @Override + public void query(PageParams params, IPage page, Long defSize) { + fileRestManager.page(page, params.getModel()); + } + + @Override + public R handlerSave(FolderSaveDTO model) { + FolderDTO folder = baseService.saveFolder(model); + return success(BeanPlusUtil.toBean(folder, File.class)); + } + + /** + * 上传文件 + * + * @param + * @return + * @author zuihou + * @date 2019-05-06 16:28 + */ + @ApiOperation(value = "上传文件", notes = "上传文件 ") + @ApiResponses({ + @ApiResponse(code = 60102, message = "文件夹为空"), + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "folderId", value = "文件夹id", dataType = "long", paramType = "query"), + @ApiImplicitParam(name = "file", value = "附件", dataType = "MultipartFile", allowMultiple = true, required = true), + }) + @RequestMapping(value = "/upload", method = RequestMethod.POST) + @SysLog("上传文件") + public R upload( + @NotNull(message = "文件夹不能为空") + @RequestParam(value = "folderId") Long folderId, + @RequestParam(value = "file") MultipartFile simpleFile) { + //1,先将文件存在本地,并且生成文件名 + log.info("contentType={}, name={} , sfname={}", simpleFile.getContentType(), simpleFile.getName(), simpleFile.getOriginalFilename()); + // 忽略路径字段,只处理文件类型 + if (simpleFile.getContentType() == null) { + return fail("文件为空"); + } + + File file = baseService.upload(simpleFile, folderId); + + return success(file); + } + + + @Override + public R handlerUpdate(FileUpdateDTO fileUpdateDTO) { + // 判断文件名是否有 后缀 + if (StringUtils.isNotEmpty(fileUpdateDTO.getSubmittedFileName())) { + File oldFile = baseService.getById(fileUpdateDTO.getId()); + if (oldFile.getExt() != null && !fileUpdateDTO.getSubmittedFileName().endsWith(oldFile.getExt())) { + fileUpdateDTO.setSubmittedFileName(fileUpdateDTO.getSubmittedFileName() + "." + oldFile.getExt()); + } + } + File file = BeanPlusUtil.toBean(fileUpdateDTO, File.class); + + baseService.updateById(file); + return success(file); + } + + @Override + public R handlerDelete(List ids) { + Long userId = getUserId(); + return success(baseService.removeList(userId, ids)); + } + + /** + * 下载一个文件或多个文件打包下载 + * + * @param ids + * @param response + * @throws Exception + */ + @ApiOperation(value = "下载一个文件或多个文件打包下载", notes = "下载一个文件或多个文件打包下载") + @GetMapping(value = "/download", produces = "application/octet-stream") + @SysLog("下载文件") + public void download( + @ApiParam(name = "ids[]", value = "文件id 数组") + @RequestParam(value = "ids[]") Long[] ids, + HttpServletRequest request, HttpServletResponse response) throws Exception { + fileRestManager.download(request, response, ids, null); + } + +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/controller/StatisticsController.java b/blade-service/blade-file/src/main/java/org/springblade/file/controller/StatisticsController.java new file mode 100644 index 0000000000000000000000000000000000000000..1b1ca947c5c01de1ade39b64ae6feeaa1d7fd76e --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/controller/StatisticsController.java @@ -0,0 +1,81 @@ +package org.springblade.file.controller; + +import com.github.zuihou.base.R; +import com.github.zuihou.file.domain.FileStatisticsDO; +import com.github.zuihou.file.dto.FileOverviewDTO; +import com.github.zuihou.file.dto.FileStatisticsAllDTO; +import com.github.zuihou.file.service.FileService; +import com.github.zuihou.security.annotation.LoginUser; +import com.github.zuihou.security.model.SysUser; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import springfox.documentation.annotations.ApiIgnore; + +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * 文件用户大小表 前端控制器 + *

+ * + * @author zuihou + * @since 2019-05-07 + */ +@Slf4j +@RestController +@RequestMapping("/statistics") +@Api(value = "Statistics", tags = "统计接口") +public class StatisticsController { + + @Autowired + private FileService fileService; + + @ApiOperation(value = "云盘首页数据概览", notes = "云盘首页数据概览") + @GetMapping(value = "/overview") + public R overview(@ApiIgnore @LoginUser SysUser user) { + return R.success(fileService.findOverview(user.getId(), null, null)); + } + + @ApiOperation(value = "按照类型,统计各种类型的 大小和数量", notes = "按照类型,统计当前登录人各种类型的大小和数量") + @GetMapping(value = "/type") + public R> findAllByDataType(@ApiIgnore @LoginUser SysUser user) { + return R.success(fileService.findAllByDataType(user.getId())); + } + +// @ApiOperation(value = "云盘首页个人文件下载数量排行", notes = "云盘首页个人文件下载数量排行") +// @GetMapping(value = "/downTop20") +// public R> downTop20() { +// return success(fileService.downTop20(getUserId())); +// } + + @ApiOperation(value = "按照时间统计各种类型的文件的数量和大小", notes = "按照时间统计各种类型的文件的数量和大小 不指定时间,默认查询一个月") + @GetMapping(value = "") + public R findNumAndSizeToTypeByDate(@RequestParam(value = "startTime", required = false) LocalDateTime startTime, + @RequestParam(value = "endTime", required = false) LocalDateTime endTime, + @ApiIgnore @LoginUser SysUser user) { + return R.success(fileService.findNumAndSizeToTypeByDate(user.getId(), startTime, endTime)); + } + +// @ApiOperation(value = "按照时间统计下载数量", notes = "按照时间统计下载数量 不指定时间,默认查询一个月") +// @GetMapping(value = "/down") +// public R findDownSizeByDate(@RequestParam(value = "startTime", required = false) LocalDateTime startTime, +// @RequestParam(value = "endTime", required = false) LocalDateTime endTime) { +// Long userId = getUserId(); +// return success(fileService.findDownSizeByDate(userId, startTime, endTime)); +// } + + + @GetMapping(value = "/test1") + public R test1(@RequestParam(value = "sleep", required = false, defaultValue = "10000") Long sleep) throws Exception { + Thread.sleep(sleep); + return R.success("等了" + sleep); + } + +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/manager/FileRestManager.java b/blade-service/blade-file/src/main/java/org/springblade/file/manager/FileRestManager.java new file mode 100644 index 0000000000000000000000000000000000000000..7a4a86b3799100b63ddaec76997168fa42178db3 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/manager/FileRestManager.java @@ -0,0 +1,58 @@ +package org.springblade.file.manager; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.github.zuihou.file.service.FileService; +import org.apache.commons.lang3.StringUtils; +import org.springblade.file.dto.FilePageReqDTO; +import org.springblade.file.entity.File; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + + +/** + * 文件 公共代码 管理类 + * + * @author zuihou + * @date 2019/05/21 + */ +@Component +public class FileRestManager { + @Autowired + private FileService fileService; + + public IPage page(IPage page, FilePageReqDTO filePageReq) { + //查询文件分页数据 + Long userId = BaseContextHandler.getUserId(); + + //类型和文件夹id同时为null时, 表示查询 全部文件 + if (filePageReq.getFolderId() == null && filePageReq.getDataType() == null) { + filePageReq.setFolderId(DEF_PARENT_ID); + } + + QueryWrapper query = new QueryWrapper<>(); + LambdaQueryWrapper lambdaQuery = query.lambda() + .eq(File::getIsDelete, false) + .eq(filePageReq.getDataType() != null, File::getDataType, filePageReq.getDataType()) + .eq(filePageReq.getFolderId() != null, File::getFolderId, filePageReq.getFolderId()) + .like(StringUtils.isNotEmpty(filePageReq.getSubmittedFileName()), File::getSubmittedFileName, filePageReq.getSubmittedFileName()) + .eq(userId != null && userId != 0, File::getCreateUser, userId); + + query.orderByDesc(String.format("case when %s='DIR' THEN 1 else 0 end", FileConstants.DATA_TYPE)); + lambdaQuery.orderByDesc(File::getCreateTime); + + fileService.page(page, lambdaQuery); + + return page; + } + + public void download(HttpServletRequest request, HttpServletResponse response, Long[] ids, Long userId) throws Exception { + userId = userId == null || userId <= 0 ? BaseContextHandler.getUserId() : userId; + fileService.download(request, response, ids, userId); + } +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/manager/WebUploader.java b/blade-service/blade-file/src/main/java/org/springblade/file/manager/WebUploader.java new file mode 100644 index 0000000000000000000000000000000000000000..db2652be655a5794727116974e7312d5be71ce72 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/manager/WebUploader.java @@ -0,0 +1,168 @@ +package org.springblade.file.manager; + + +import com.github.zuihou.file.dto.chunk.FileUploadDTO; +import lombok.extern.slf4j.Slf4j; +import org.springblade.file.dto.chunk.FileUploadDTO; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * 分片上传工具类 + * + * @author zuihou + * @date 2019-06-14 11:50 + */ +@Service +@Scope("prototype") +@Slf4j +public class WebUploader { + + /** + * 错误详情 + */ + private String msg; + + /** + * 分片验证 + * 验证对应分片文件是否存在,大小是否吻合 + * + * @param file 分片文件的路径 + * @param size 分片文件的大小 + * @return + */ + public boolean chunkCheck(String file, Long size) { + //检查目标分片是否存在且完整 + java.io.File target = new java.io.File(file); + return target.isFile() && size == target.length(); + } + + /** + * 为上传的文件创建对应的保存位置 + * 若上传的是分片,则会创建对应的文件夹结构和tmp文件 + * + * @param info 上传文件的相关信息 + * @param path 文件保存根路径 + * @return + */ + public java.io.File getReadySpace(FileUploadDTO info, String path) { + //创建上传文件所需的文件夹 + if (!this.createFileFolder(path, false)) { + return null; + } + + String newFileName; //上传文件的新名称 + + //如果是分片上传,则需要为分片创建文件夹 + if (info.getChunks() > 0) { + newFileName = String.valueOf(info.getChunk()); + + String fileFolder = this.md5(info.getName() + info.getType() + info.getLastModifiedDate() + info.getSize()); + log.info("fileFolder={}, md5={}", fileFolder, info.getMd5()); + if (fileFolder == null) { + return null; + } + + //文件上传路径更新为指定文件信息签名后的临时文件夹,用于后期合并 + path += "/" + fileFolder; + + if (!this.createFileFolder(path, true)) { + return null; + } + return new java.io.File(path, newFileName); + } + return null; + } + + /** + * 创建存放上传的文件的文件夹 + * + * @param file 文件夹路径 + * @param hasTmp 是否有临时文件 + * @return + */ + private boolean createFileFolder(String file, boolean hasTmp) { + //创建存放分片文件的临时文件夹 + java.io.File tmpFile = new java.io.File(file); + if (!tmpFile.exists()) { + try { + tmpFile.mkdirs(); + } catch (SecurityException ex) { + log.error("无法创建文件夹", ex); + this.setErrorMsg("无法创建文件夹"); + return false; + } + } + + if (hasTmp) { + //创建一个对应的文件,用来记录上传分片文件的修改时间,用于清理长期未完成的垃圾分片 + tmpFile = new java.io.File(file + ".tmp"); + if (tmpFile.exists()) { + return tmpFile.setLastModified(System.currentTimeMillis()); + } else { + try { + tmpFile.createNewFile(); + } catch (IOException ex) { + log.error("无法创建tmp文件", ex); + this.setErrorMsg("无法创建tmp文件"); + return false; + } + } + } + + return true; + } + + + /** + * MD5签名 + * + * @param content 要签名的内容 + * @return + */ + private String md5(String content) { + StringBuffer sb = new StringBuffer(); + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(content.getBytes("UTF-8")); + byte[] tmpFolder = md5.digest(); + + for (int i = 0; i < tmpFolder.length; i++) { + sb.append(Integer.toString((tmpFolder[i] & 0xff) + 0x100, 16).substring(1)); + } + + return sb.toString(); + } catch (NoSuchAlgorithmException ex) { + log.error("无法生成文件的MD5签名", ex); + this.setErrorMsg("无法生成文件的MD5签名"); + return null; + } catch (UnsupportedEncodingException ex) { + log.error("无法生成文件的MD5签名", ex); + this.setErrorMsg("无法生成文件的MD5签名"); + return null; + } + } + + /** + * 获取错误详细 + * + * @return + */ + public String getErrorMsg() { + return this.msg; + } + + /** + * 记录异常错误信息 + * + * @param msg 错误详细 + */ + private void setErrorMsg(String msg) { + this.msg = msg; + } +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/mapper/AttachmentMapper.java b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/AttachmentMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..7984ed44741fafbc12fc3dabacc716fd1cfb0acc --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/AttachmentMapper.java @@ -0,0 +1,54 @@ +package org.springblade.file.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.springblade.file.authScope.DataScope; +import org.springblade.file.dto.AttachmentResultDTO; +import org.springblade.file.entity.Attachment; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + *

+ * Mapper 接口 + * 附件 + *

+ * + * @author zuihou + * @date 2019-06-24 + */ +@Repository +public interface AttachmentMapper extends BaseMapper { + /** + * 根据业务类型和业务id, 按照分组查询附件 + * + * @param bizTypes + * @param bizIds + * @return + */ + List find(@Param("bizTypes") String[] bizTypes, @Param("bizIds") String[] bizIds); + + /** + * 查询不在指定id集合中的数据 + * + * @param ids + * @param group + * @param path + * @return + */ + Integer countByGroup(@Param("ids") List ids, @Param("group") String group, @Param("path") String path); + + /** + * 按权限查询数据 + * + * @param page + * @param wrapper + * @param dataScope + * @return + */ + IPage page(IPage page, @Param(Constants.WRAPPER) Wrapper wrapper, DataScope dataScope); +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/mapper/AttachmentMapper.xml b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/AttachmentMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..a50bdec0518ab7c0c909f31bf753fe948ab564ea --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/AttachmentMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, create_time, create_user, update_time, update_user, + biz_id, biz_type, data_type, submitted_file_name, group_, path, relative_path, url, + file_md5, context_type, filename, ext, size, org_id, icon, create_month, create_week, create_day + + + diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/mapper/FileMapper.java b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/FileMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..b43cad27756cae4dbb3de71e262460d396d9583a --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/FileMapper.java @@ -0,0 +1,72 @@ +package org.springblade.file.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; +import org.springblade.file.domain.FileQueryDO; +import org.springblade.file.domain.FileStatisticsDO; +import org.springblade.file.entity.File; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * Mapper 接口 + * 文件表 + *

+ * + * @author zuihou + * @date 2019-06-24 + */ +@Repository +public interface FileMapper extends BaseMapper { + /** + * 查询文件以及它的父类 + * + * @param userId + * @param ids + * @return + * @author zuihou + * @date 2019-05-07 20:49 + */ + List findByIds(@Param("userId") Long userId, @Param("ids") Long[] ids); + + /** + * 按照日期类型,时间区间,来查询指定用户的各种类型的 数量和大小 + * + * @param userId + * @param dateType 日期类型 + * @param dataType 数据类型 数据类型=ALL 按类型统计所有, =指定类型时(不等null), 统计指定类型 , =null 时不区分类型统计所有 + * @param startTime + * @param endTime + * @return FileStatisticsDO + */ + List findNumAndSizeByUserId(@Param("userId") Long userId, + @Param("dateType") String dateType, + @Param("dataType") String dataType, + @Param("startTime") LocalDateTime startTime, + @Param("endTime") LocalDateTime endTime); + + /** + * 查询下次次数前20的文件 + * + * @param userId + * @return + */ + List findDownTop20(@Param("userId") Long userId); + + /** + * 统计时间区间内文 + * @param userId + * @param dateType 日期类型 {MONTH:按月;WEEK:按周;DAY:按日} 来统计 + * @param startTime + * @param endTime + * @return + */ + List findDownSizeByDate(@Param("userId") Long userId, + @Param("dateType") String dateType, + @Param("startTime") LocalDateTime startTime, + @Param("endTime") LocalDateTime endTime); +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/mapper/FileMapper.xml b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/FileMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..a6bc70da9d2c2d04bec1b1e3aeea2523b6000b20 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/FileMapper.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, create_time, create_user, update_time, update_user, + data_type, submitted_file_name, tree_path, grade, is_delete, folder_id, url, size, folder_name, group_, path, relative_path, file_md5, context_type, filename, ext, icon, create_month, create_week, create_day + + + diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/mapper/ext/AttachmentMapper.xml b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/ext/AttachmentMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..fe55bc4be280b85e3032db13264d4d8dd0b0bd08 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/ext/AttachmentMapper.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/mapper/ext/FileMapper.xml b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/ext/FileMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..533743414e1049bba73f90d44c105829c70e02e2 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/mapper/ext/FileMapper.xml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/properties/FileServerProperties.java b/blade-service/blade-file/src/main/java/org/springblade/file/properties/FileServerProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..19bb14af4125ee51c6b0998437154c26d725c5a8 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/properties/FileServerProperties.java @@ -0,0 +1,86 @@ +package org.springblade.file.properties; + + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import org.springblade.file.enumeration.FileStorageType; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; + +import java.io.File; + + +/** + * @author zuihou + */ +@Setter +@Getter +@ConfigurationProperties(prefix = FileServerProperties.PREFIX) +@RefreshScope +public class FileServerProperties { + public static final String PREFIX = "zuihou.file"; + /** + * 为以下3个值,指定不同的自动化配置 + * qiniu:七牛oss + * aliyun:阿里云oss + * fastdfs:本地部署的fastDFS + */ + private FileStorageType type = FileStorageType.LOCAL; + /** + * 文件访问前缀 + */ + private String uriPrefix = "http://127.0.0.1:10000/"; + /** + * 文件存储路径 + */ + private String storagePath = "/data/projects/uploadfile/file/"; + /** + * 内网通道前缀 主要用于解决某些服务器的无法访问外网ip的问题 + */ + private String innerUriPrefix = ""; + private String downByUrl = ""; + private String downByBizId = ""; + private String downById = ""; + + private Ali ali; + + public String getDownByUrl(Object... param) { + return String.format(downByUrl, param); + } + + public String getDownByBizId(Object... param) { + return String.format(downByBizId, param); + } + + public String getDownById(Object... param) { + return String.format(downById, param); + } + + public String getInnerUriPrefix() { + return innerUriPrefix; + } + + public String getUriPrefix() { + if (!uriPrefix.endsWith(StrPool.SLASH)) { + uriPrefix += StrPool.SLASH; + } + return uriPrefix; + } + + public String getStoragePath() { + if (!storagePath.endsWith(File.separator)) { + storagePath += File.separator; + } + return storagePath; + } + + @Data + public static class Ali { + private String uriPrefix; + private String endpoint; + private String accessKeyId; + private String accessKeySecret; + private String bucketName; + } +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/service/AttachmentService.java b/blade-service/blade-file/src/main/java/org/springblade/file/service/AttachmentService.java new file mode 100644 index 0000000000000000000000000000000000000000..859457f04d7cfa1290eb2694de53d285f1b9eff7 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/service/AttachmentService.java @@ -0,0 +1,102 @@ +package com.github.zuihou.file.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.github.zuihou.base.service.SuperService; +import com.github.zuihou.file.dto.AttachmentDTO; +import com.github.zuihou.file.dto.AttachmentResultDTO; +import com.github.zuihou.file.dto.FilePageReqDTO; +import com.github.zuihou.file.entity.Attachment; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + *

+ * 业务接口 + * 附件 + *

+ * + * @author zuihou + * @date 2019-06-24 + */ +public interface AttachmentService extends SuperService { + /** + * 上传附件 + * + * @param file 文件 + * @param tenant 租户 + * @param id 附件id + * @param bizType 业务类型 + * @param bizId 业务id + * @param isSingle 是否单个文件 + * @return + */ + AttachmentDTO upload(MultipartFile file, String tenant, Long id, String bizType, String bizId, Boolean isSingle); + + /** + * 删除附件 + * + * @param ids + */ + boolean remove(List ids); + + /** + * 根据业务id和业务类型删除附件 + * + * @param bizId + * @param bizType + */ + boolean removeByBizIdAndBizType(String bizId, String bizType); + + /** + * 根据业务类型和业务id查询附件 + * + * @param bizTypes + * @param bizIds + * @return + */ + List find(String[] bizTypes, String[] bizIds); + + /** + * 根据文件id下载附件 + * + * @param request + * @param response + * @param ids + * @throws Exception + */ + void download(HttpServletRequest request, HttpServletResponse response, Long[] ids) throws Exception; + + /** + * 根据业务id和业务类型下载附件 + * + * @param request + * @param response + * @param bizTypes + * @param bizIds + * @throws Exception + */ + void downloadByBiz(HttpServletRequest request, HttpServletResponse response, String[] bizTypes, String[] bizIds) throws Exception; + + /** + * 根据文件url下载附件 + * + * @param request + * @param response + * @param url + * @param filename + * @throws Exception + */ + void downloadByUrl(HttpServletRequest request, HttpServletResponse response, String url, String filename) throws Exception; + + /** + * 查询附件分页数据,按权限 + * + * @param page + * @param data + * @return + */ + IPage page(IPage page, FilePageReqDTO data); +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/service/FileService.java b/blade-service/blade-file/src/main/java/org/springblade/file/service/FileService.java new file mode 100644 index 0000000000000000000000000000000000000000..50d79a2accad0ae50ea56c2fcf3b80e30f14a561 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/service/FileService.java @@ -0,0 +1,131 @@ +package com.github.zuihou.file.service; + +import com.github.zuihou.base.service.SuperService; +import com.github.zuihou.file.domain.FileAttrDO; +import com.github.zuihou.file.domain.FileStatisticsDO; +import com.github.zuihou.file.dto.FileOverviewDTO; +import com.github.zuihou.file.dto.FileStatisticsAllDTO; +import com.github.zuihou.file.dto.FolderDTO; +import com.github.zuihou.file.dto.FolderSaveDTO; +import com.github.zuihou.file.entity.File; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * 业务接口 + * 文件表 + *

+ * + * @author zuihou + * @date 2019-06-24 + */ +public interface FileService extends SuperService { + /** + * 保存文件夹 + * + * @param folderSaveDto 文件夹 + * @return + */ + FolderDTO saveFolder(FolderSaveDTO folderSaveDto); + + /** + * 根据文件id下载文件,并统计下载次数 + * + * @param request 请求 + * @param response 响应 + * @param ids 文件id集合 + * @param userId 用户id + * @throws Exception + */ + void download(HttpServletRequest request, HttpServletResponse response, + Long[] ids, Long userId) throws Exception; + + /** + * 根据文件id和用户id 删除文件或者文件夹 + * + * @param userId 用户id + * @param ids 文件id集合 + * @return + */ + Boolean removeList(Long userId, List ids); + + /** + * 根据文件夹id查询 + * + * @param folderId + * @return + */ + FileAttrDO getFileAttrDo(Long folderId); + + /** + * 文件上传 + * + * @param simpleFile 文件 + * @param folderId 文件夹id + * @return + */ + File upload(MultipartFile simpleFile, Long folderId); + + /** + * 首页概览 + * + * @param userId + * @param startTime + * @param endTime + * @return + */ + FileOverviewDTO findOverview(Long userId, LocalDateTime startTime, LocalDateTime endTime); + + /** + * 首页个人文件发展概览 + * + * @param userId + * @param startTime + * @param endTime + * @return + */ + FileStatisticsAllDTO findAllByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime); + + + /** + * 按照 数据类型分类查询 当前人的所有文件的数量和大小 + * + * @param userId + * @return + */ + List findAllByDataType(Long userId); + + /** + * 查询下载排行前20的文件 + * + * @param userId + * @return + */ + List downTop20(Long userId); + + /** + * 根据日期查询,特定类型的数量和大小 + * + * @param userId + * @param startTime + * @param endTime + * @return + */ + FileStatisticsAllDTO findNumAndSizeToTypeByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime); + + /** + * 根据日期查询下载大小 + * + * @param userId + * @param startTime + * @param endTime + * @return + */ + FileStatisticsAllDTO findDownSizeByDate(Long userId, LocalDateTime startTime, + LocalDateTime endTime); +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/service/impl/AttachmentServiceImpl.java b/blade-service/blade-file/src/main/java/org/springblade/file/service/impl/AttachmentServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..8ba2dbf05e6870bcebf16bab61d07fc3f9fd7672 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/service/impl/AttachmentServiceImpl.java @@ -0,0 +1,201 @@ +package com.github.zuihou.file.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.github.zuihou.base.service.SuperServiceImpl; +import com.github.zuihou.database.mybatis.auth.DataScope; +import com.github.zuihou.database.mybatis.conditions.Wraps; +import com.github.zuihou.database.mybatis.conditions.query.LbqWrapper; +import com.github.zuihou.database.properties.DatabaseProperties; +import com.github.zuihou.exception.BizException; +import com.github.zuihou.file.biz.FileBiz; +import com.github.zuihou.file.dao.AttachmentMapper; +import com.github.zuihou.file.domain.FileDO; +import com.github.zuihou.file.domain.FileDeleteDO; +import com.github.zuihou.file.dto.AttachmentDTO; +import com.github.zuihou.file.dto.AttachmentResultDTO; +import com.github.zuihou.file.dto.FilePageReqDTO; +import com.github.zuihou.file.entity.Attachment; +import com.github.zuihou.file.entity.File; +import com.github.zuihou.file.enumeration.DataType; +import com.github.zuihou.file.properties.FileServerProperties; +import com.github.zuihou.file.service.AttachmentService; +import com.github.zuihou.file.strategy.FileStrategy; +import com.github.zuihou.utils.BeanPlusUtil; +import com.github.zuihou.utils.DateUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * 业务实现类 + * 附件 + *

+ * + * @author zuihou + * @date 2019-06-24 + */ +@Slf4j +@Service + +public class AttachmentServiceImpl extends SuperServiceImpl implements AttachmentService { + @Autowired + private DatabaseProperties databaseProperties; + @Resource + private FileStrategy fileStrategy; + @Autowired + private FileServerProperties fileProperties; + @Autowired + private FileBiz fileBiz; + + @Override + public IPage page(IPage page, FilePageReqDTO data) { + Attachment attachment = BeanPlusUtil.toBean(data, Attachment.class); + + // ${ew.customSqlSegment} 语法一定要手动eq like 等 不能用lbQ! + LbqWrapper wrapper = Wraps.lbQ() + .like(Attachment::getSubmittedFileName, attachment.getSubmittedFileName()) + .like(Attachment::getBizType, attachment.getBizType()) + .like(Attachment::getBizId, attachment.getBizId()) + .eq(Attachment::getDataType, attachment.getDataType()) + .orderByDesc(Attachment::getId); + return baseMapper.page(page, wrapper, new DataScope()); + } + + @Override + public AttachmentDTO upload(MultipartFile multipartFile, String tenant, Long id, String bizType, String bizId, Boolean isSingle) { + //根据业务类型来判断是否生成业务id + if (StringUtils.isNotEmpty(bizType) && StringUtils.isEmpty(bizId)) { + DatabaseProperties.Id idPro = databaseProperties.getId(); + bizId = IdUtil.getSnowflake(idPro.getWorkerId(), idPro.getDataCenterId()).nextIdStr(); + } + File file = fileStrategy.upload(multipartFile); + + Attachment attachment = BeanPlusUtil.toBean(file, Attachment.class); + + attachment.setBizId(bizId); + attachment.setBizType(bizType); + setDate(attachment); + + if (isSingle) { + super.remove(Wraps.lbQ().eq(Attachment::getBizId, bizId).eq(Attachment::getBizType, bizType)); + } + + if (id != null && id > 0) { + //当前端传递了文件id时,修改这条记录 + attachment.setId(id); + super.updateById(attachment); + } else { + super.save(attachment); + } + + AttachmentDTO dto = BeanPlusUtil.toBean(attachment, AttachmentDTO.class); + dto.setDownloadUrlByBizId(fileProperties.getDownByBizId(bizId)); + dto.setDownloadUrlById(fileProperties.getDownById(attachment.getId())); + dto.setDownloadUrlByUrl(fileProperties.getDownByUrl(attachment.getUrl(), attachment.getSubmittedFileName())); + return dto; + } + + private void setDate(Attachment file) { + LocalDateTime now = LocalDateTime.now(); + file.setCreateMonth(DateUtils.formatAsYearMonthEn(now)); + file.setCreateWeek(DateUtils.formatAsYearWeekEn(now)); + file.setCreateDay(DateUtils.formatAsDateEn(now)); + } + + @Override + public boolean remove(List ids) { + if (CollectionUtil.isEmpty(ids)) { + return false; + } + + List list = super.list(Wrappers.lambdaQuery().in(Attachment::getId, ids)); + if (list.isEmpty()) { + return false; + } + boolean bool = super.removeByIds(ids); + + boolean boolDel = fileStrategy.delete(list.stream().map((fi) -> FileDeleteDO.builder() + .relativePath(fi.getRelativePath()) + .fileName(fi.getFilename()) + .group(fi.getGroup()) + .path(fi.getPath()) + .file(false) + .build()) + .collect(Collectors.toList())); + return bool && boolDel; + } + + @Override + public boolean removeByBizIdAndBizType(String bizId, String bizType) { + List list = super.list( + Wraps.lbQ() + .eq(Attachment::getBizId, bizId) + .eq(Attachment::getBizType, bizType)); + if (list.isEmpty()) { + return false; + } + return remove(list.stream().mapToLong(Attachment::getId).boxed().collect(Collectors.toList())); + } + + @Override + public List find(String[] bizTypes, String[] bizIds) { + return baseMapper.find(bizTypes, bizIds); + } + + @Override + public void download(HttpServletRequest request, HttpServletResponse response, Long[] ids) throws Exception { + List list = (List) super.listByIds(Arrays.asList(ids)); + down(request, response, list); + } + + @Override + public void downloadByBiz(HttpServletRequest request, HttpServletResponse response, String[] bizTypes, String[] bizIds) throws Exception { + List list = super.list( + Wraps.lbQ() + .in(Attachment::getBizType, bizTypes) + .in(Attachment::getBizId, bizIds)); + + down(request, response, list); + } + + @Override + public void downloadByUrl(HttpServletRequest request, HttpServletResponse response, String url, String filename) throws Exception { + if (StringUtils.isEmpty(filename)) { + filename = "未知文件名.txt"; + } + List list = Arrays.asList(Attachment.builder() + .url(url).submittedFileName(filename).size(0L).dataType(DataType.DOC).build()); + down(request, response, list); + } + + private void down(HttpServletRequest request, HttpServletResponse response, List list) throws Exception { + if (list.isEmpty()) { + throw BizException.wrap("您下载的文件不存在"); + } + List listDO = list.stream().map((file) -> + FileDO.builder() + .url(file.getUrl()) + .submittedFileName(file.getSubmittedFileName()) + .size(file.getSize()) + .dataType(file.getDataType()) + .build()) + .collect(Collectors.toList()); + fileBiz.down(listDO, request, response); + } + + +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/service/impl/FileServiceImpl.java b/blade-service/blade-file/src/main/java/org/springblade/file/service/impl/FileServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..920b2ef74af27d9633b035920679d77afc63b783 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/service/impl/FileServiceImpl.java @@ -0,0 +1,432 @@ +package com.github.zuihou.file.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.github.zuihou.base.service.SuperServiceImpl; +import com.github.zuihou.database.mybatis.conditions.Wraps; +import com.github.zuihou.database.mybatis.conditions.update.LbuWrapper; +import com.github.zuihou.file.biz.FileBiz; +import com.github.zuihou.file.dao.FileMapper; +import com.github.zuihou.file.domain.FileAttrDO; +import com.github.zuihou.file.domain.FileDO; +import com.github.zuihou.file.domain.FileDeleteDO; +import com.github.zuihou.file.domain.FileStatisticsDO; +import com.github.zuihou.file.dto.FileOverviewDTO; +import com.github.zuihou.file.dto.FileStatisticsAllDTO; +import com.github.zuihou.file.dto.FolderDTO; +import com.github.zuihou.file.dto.FolderSaveDTO; +import com.github.zuihou.file.entity.File; +import com.github.zuihou.file.enumeration.DataType; +import com.github.zuihou.file.enumeration.IconType; +import com.github.zuihou.file.service.FileService; +import com.github.zuihou.file.strategy.FileStrategy; +import com.github.zuihou.utils.BeanPlusUtil; +import com.github.zuihou.utils.BizAssert; +import com.github.zuihou.utils.DateUtils; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.github.zuihou.exception.code.ExceptionCode.BASE_VALID_PARAM; +import static com.github.zuihou.utils.StrPool.DEF_PARENT_ID; +import static com.github.zuihou.utils.StrPool.DEF_ROOT_PATH; +import static java.util.stream.Collectors.groupingBy; + +/** + *

+ * 业务实现类 + * 文件表 + *

+ * + * @author zuihou + * @date 2019-06-24 + */ +@Slf4j +@Service + +public class FileServiceImpl extends SuperServiceImpl implements FileService { + + @Autowired + private FileBiz fileBiz; + @Resource + private FileStrategy fileStrategy; + + @Override + public File upload(MultipartFile simpleFile, Long folderId) { + FileAttrDO fileAttrDO = this.getFileAttrDo(folderId); + String treePath = fileAttrDO.getTreePath(); + String folderName = fileAttrDO.getFolderName(); + Integer grade = fileAttrDO.getGrade(); + + File file = fileStrategy.upload(simpleFile); + file.setFolderId(folderId); + file.setFolderName(folderName); + file.setGrade(grade); + file.setTreePath(treePath); + super.save(file); + return file; + } + + @Override + public FileAttrDO getFileAttrDo(Long folderId) { + String treePath = DEF_ROOT_PATH; + String folderName = ""; + Integer grade = 1; + if (folderId == null || folderId <= 0) { + return new FileAttrDO(treePath, grade, folderName, DEF_PARENT_ID); + } + File folder = this.getById(folderId); + + if (folder != null && !folder.getIsDelete() && DataType.DIR.eq(folder.getDataType())) { + folderName = folder.getSubmittedFileName(); + treePath = StringUtils.join(folder.getTreePath(), folder.getId(), DEF_ROOT_PATH); + grade = folder.getGrade() + 1; + } + BizAssert.isTrue(grade <= 10, BASE_VALID_PARAM.build("文件夹层级不能超过10层")); + return new FileAttrDO(treePath, grade, folderName, folderId); + } + + + @Override + public FolderDTO saveFolder(FolderSaveDTO folderSaveDto) { + File folder = BeanPlusUtil.toBean(folderSaveDto, File.class); + if (folderSaveDto.getFolderId() == null || folderSaveDto.getFolderId() <= 0) { + folder.setFolderId(DEF_PARENT_ID); + folder.setTreePath(DEF_ROOT_PATH); + folder.setGrade(1); + } else { + File parent = super.getById(folderSaveDto.getFolderId()); + BizAssert.notNull(parent, BASE_VALID_PARAM.build("父文件夹不能为空")); + BizAssert.isFalse(parent.getIsDelete(), BASE_VALID_PARAM.build("父文件夹已经被删除")); + BizAssert.equals(DataType.DIR.name(), parent.getDataType().name(), BASE_VALID_PARAM.build("父文件夹不存在")); + BizAssert.isTrue(parent.getGrade() < 10, BASE_VALID_PARAM.build("文件夹层级不能超过10层")); + folder.setFolderName(parent.getSubmittedFileName()); + folder.setTreePath(StringUtils.join(parent.getTreePath(), parent.getId(), DEF_ROOT_PATH)); + folder.setGrade(parent.getGrade() + 1); + } + if (folderSaveDto.getOrderNum() == null) { + folderSaveDto.setOrderNum(0); + } + folder.setIsDelete(false); + folder.setDataType(DataType.DIR); + folder.setIcon(IconType.DIR.getIcon()); + setDate(folder); + super.save(folder); + return BeanPlusUtil.toBean(folder, FolderDTO.class); + } + + private void setDate(File file) { + LocalDateTime now = LocalDateTime.now(); + file.setCreateMonth(DateUtils.formatAsYearMonthEn(now)) + .setCreateWeek(DateUtils.formatAsYearWeekEn(now)) + .setCreateDay(DateUtils.formatAsDateEn(now)); + } + + public boolean removeFile(Long[] ids, Long userId) { + LbuWrapper lambdaUpdate = + Wraps.lbU() + .in(File::getId, ids) + .eq(File::getCreateUser, userId); + File file = File.builder().isDelete(Boolean.TRUE).build(); + + return super.update(file, lambdaUpdate); + } + + @Override + public Boolean removeList(Long userId, List ids) { + if (CollectionUtil.isEmpty(ids)) { + return Boolean.TRUE; + } + List list = super.list(Wrappers.lambdaQuery().in(File::getId, ids)); + if (list.isEmpty()) { + return true; + } + super.removeByIds(ids); + + fileStrategy.delete(list.stream().map((fi) -> FileDeleteDO.builder() + .relativePath(fi.getRelativePath()) + .fileName(fi.getFilename()) + .group(fi.getGroup()) + .path(fi.getPath()) + .file(false) + .build()) + .collect(Collectors.toList())); + return true; + } + + @Override + public void download(HttpServletRequest request, HttpServletResponse response, Long[] ids, Long userId) throws Exception { + if (ids == null || ids.length == 0) { + return; + } + List list = (List) super.listByIds(Arrays.asList(ids)); + + if (list == null || list.size() == 0) { + return; + } + List listDo = list.stream().map((file) -> + FileDO.builder() + .dataType(file.getDataType()) + .size(file.getSize()) + .submittedFileName(file.getSubmittedFileName()) + .url(file.getUrl()) + .build()) + .collect(Collectors.toList()); + fileBiz.down(listDo, request, response); + } + + + @Override + public FileOverviewDTO findOverview(Long userId, LocalDateTime startTime, LocalDateTime endTime) { + InnerQueryDate innerQueryDate = new InnerQueryDate(userId, startTime, endTime).invoke(); + startTime = innerQueryDate.getStartTime(); + endTime = innerQueryDate.getEndTime(); + + List list = baseMapper.findNumAndSizeByUserId(userId, null, "ALL", startTime, endTime); + FileOverviewDTO.FileOverviewDTOBuilder builder = FileOverviewDTO.myBuilder(); + + long allSize = 0L; + int allNum = 0; + for (FileStatisticsDO fs : list) { + allSize += fs.getSize(); + allNum += fs.getNum(); + switch (fs.getDataType()) { + case DIR: + builder.dirNum(fs.getNum()); + break; + case IMAGE: + builder.imgNum(fs.getNum()); + break; + case VIDEO: + builder.videoNum(fs.getNum()); + break; + case DOC: + builder.docNum(fs.getNum()); + break; + case AUDIO: + builder.audioNum(fs.getNum()); + break; + case OTHER: + builder.otherNum(fs.getNum()); + break; + default: + break; + } + } + builder.allFileNum(allNum).allFileSize(allSize); + return builder.build(); + } + + @Override + public FileStatisticsAllDTO findAllByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime) { + InnerQueryDate innerQueryDate = new InnerQueryDate(userId, startTime, endTime).invoke(); + startTime = innerQueryDate.getStartTime(); + endTime = innerQueryDate.getEndTime(); + List dateList = innerQueryDate.getDateList(); + String dateType = innerQueryDate.getDateType(); + + //不完整的数据 + List list = baseMapper.findNumAndSizeByUserId(userId, dateType, null, startTime, endTime); + + //按月份分类 + Map> map = list.stream().collect(groupingBy(FileStatisticsDO::getDateType)); + + List sizeList = new ArrayList<>(); + List numList = new ArrayList<>(); + + dateList.forEach((date) -> { + if (map.containsKey(date)) { + List subList = map.get(date); + + Long size = subList.stream().mapToLong(FileStatisticsDO::getSize).sum(); + Integer num = subList.stream().filter((fs) -> !DataType.DIR.eq(fs.getDataType())) + .mapToInt(FileStatisticsDO::getNum).sum(); + sizeList.add(size); + numList.add(num); + } else { + sizeList.add(0L); + numList.add(0); + } + }); + + return FileStatisticsAllDTO.builder().dateList(dateList).numList(numList).sizeList(sizeList).build(); + } + + + @Override + public List findAllByDataType(Long userId) { + List dataTypes = Arrays.asList(DataType.values()); + List list = baseMapper.findNumAndSizeByUserId(userId, null, "ALL", null, null); + + Map> map = list.stream().collect(groupingBy(FileStatisticsDO::getDataType)); + + return dataTypes.stream().map((type) -> { + FileStatisticsDO fs = null; + if (map.containsKey(type)) { + fs = map.get(type).get(0); + } else { + fs = FileStatisticsDO.builder().dataType(type).size(0L).num(0).build(); + } + return fs; + }).collect(Collectors.toList()); + } + + @Override + public List downTop20(Long userId) { + return baseMapper.findDownTop20(userId); + } + + @Override + public FileStatisticsAllDTO findNumAndSizeToTypeByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime) { + return common(userId, startTime, endTime, + (qd) -> baseMapper.findNumAndSizeByUserId(qd.getUserId(), qd.getDateType(), "ALL", qd.getStartTime(), qd.getEndTime())); + } + + @Override + public FileStatisticsAllDTO findDownSizeByDate(Long userId, LocalDateTime startTime, + LocalDateTime endTime) { + return common(userId, startTime, endTime, + (qd) -> baseMapper.findDownSizeByDate(qd.getUserId(), qd.getDateType(), qd.getStartTime(), qd.getEndTime())); + } + + /** + * 抽取公共查询公共代码 + * + * @param userId 用户id + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param function 回调函数 + * @return + */ + private FileStatisticsAllDTO common(Long userId, LocalDateTime startTime, LocalDateTime endTime, Function> function) { + InnerQueryDate innerQueryDate = new InnerQueryDate(userId, startTime, endTime).invoke(); + List dateList = innerQueryDate.getDateList(); + + List list = function.apply(innerQueryDate); + + //按月份分类 + Map> map = list.stream().collect(groupingBy(FileStatisticsDO::getDateType)); + + List sizeList = new ArrayList<>(dateList.size()); + List numList = new ArrayList<>(dateList.size()); + + List dirNumList = new ArrayList<>(dateList.size()); + + List imgSizeList = new ArrayList<>(dateList.size()); + List imgNumList = new ArrayList<>(dateList.size()); + + List videoSizeList = new ArrayList<>(dateList.size()); + List videoNumList = new ArrayList<>(dateList.size()); + + List audioSizeList = new ArrayList<>(dateList.size()); + List audioNumList = new ArrayList<>(dateList.size()); + + List docSizeList = new ArrayList<>(dateList.size()); + List docNumList = new ArrayList<>(dateList.size()); + + List otherSizeList = new ArrayList<>(dateList.size()); + List otherNumList = new ArrayList<>(dateList.size()); + + dateList.forEach((date) -> { + if (map.containsKey(date)) { + List subList = map.get(date); + + Function> stream = (dataType) -> subList.stream().filter((fs) -> !dataType.eq(fs.getDataType())); + Long size = stream.apply(DataType.DIR).mapToLong(FileStatisticsDO::getSize).sum(); + Integer num = stream.apply(DataType.DIR).mapToInt(FileStatisticsDO::getNum).sum(); + sizeList.add(size); + numList.add(num); + + Integer dirNum = subList.stream().filter((fs) -> DataType.DIR.eq(fs.getDataType())) + .mapToInt(FileStatisticsDO::getNum).sum(); + dirNumList.add(dirNum); + + add(imgSizeList, imgNumList, subList, DataType.IMAGE); + add(videoSizeList, videoNumList, subList, DataType.VIDEO); + add(audioSizeList, audioNumList, subList, DataType.AUDIO); + add(docSizeList, docNumList, subList, DataType.DOC); + add(otherSizeList, otherNumList, subList, DataType.OTHER); + + } else { + sizeList.add(0L); + numList.add(0); + dirNumList.add(0); + imgSizeList.add(0L); + imgNumList.add(0); + videoSizeList.add(0L); + videoNumList.add(0); + audioSizeList.add(0L); + audioNumList.add(0); + docSizeList.add(0L); + docNumList.add(0); + otherSizeList.add(0L); + otherNumList.add(0); + } + }); + + return FileStatisticsAllDTO.builder() + .dateList(dateList) + .numList(numList).sizeList(sizeList) + .dirNumList(dirNumList) + .imgNumList(imgNumList).imgSizeList(imgSizeList) + .videoNumList(videoNumList).videoSizeList(videoSizeList) + .audioNumList(audioNumList).audioSizeList(audioSizeList) + .docNumList(docNumList).docSizeList(docSizeList) + .otherNumList(otherNumList).otherSizeList(otherSizeList) + .build(); + } + + private void add(List sizeList, List numList, List subList, DataType dt) { + Function> stream = + dataType -> subList.stream().filter(fs -> dataType.eq(fs.getDataType())); + + Long size = stream.apply(dt).mapToLong(FileStatisticsDO::getSize).sum(); + Integer num = stream.apply(dt).mapToInt(FileStatisticsDO::getNum).sum(); + sizeList.add(size); + numList.add(num); + } + + @Getter + private static class InnerQueryDate { + private LocalDateTime startTime; + private LocalDateTime endTime; + private List dateList; + private String dateType; + private Long userId; + + public InnerQueryDate(Long userId, LocalDateTime startTime, LocalDateTime endTime) { + this.userId = userId; + this.startTime = startTime; + this.endTime = endTime; + } + + public InnerQueryDate invoke() { + if (startTime == null) { + startTime = LocalDateTime.now().plusDays(-9); + } + if (endTime == null) { + endTime = LocalDateTime.now(); + } + endTime = LocalDateTime.of(endTime.toLocalDate(), LocalTime.MAX); + dateList = new ArrayList<>(); + dateType = DateUtils.calculationEn(startTime, endTime, dateList); + return this; + } + } +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/storage/AliOssAutoConfigure.java b/blade-service/blade-file/src/main/java/org/springblade/file/storage/AliOssAutoConfigure.java new file mode 100644 index 0000000000000000000000000000000000000000..02b68f05683b5f1be2298901d99c1e588dd557fb --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/storage/AliOssAutoConfigure.java @@ -0,0 +1,254 @@ +package org.springblade.file.storage; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.aliyun.oss.model.CompleteMultipartUploadRequest; +import com.aliyun.oss.model.CompleteMultipartUploadResult; +import com.aliyun.oss.model.InitiateMultipartUploadRequest; +import com.aliyun.oss.model.InitiateMultipartUploadResult; +import com.aliyun.oss.model.ObjectMetadata; +import com.aliyun.oss.model.PartETag; +import com.aliyun.oss.model.PutObjectRequest; +import com.aliyun.oss.model.PutObjectResult; +import com.aliyun.oss.model.UploadPartCopyRequest; +import com.aliyun.oss.model.UploadPartCopyResult; +import com.aliyun.oss.model.UploadPartRequest; +import com.aliyun.oss.model.UploadPartResult; +import com.github.zuihou.file.domain.FileDeleteDO; +import com.github.zuihou.file.dto.chunk.FileChunksMergeDTO; +import com.github.zuihou.file.entity.File; +import com.github.zuihou.file.properties.FileServerProperties; +import com.github.zuihou.file.strategy.impl.AbstractFileChunkStrategy; +import com.github.zuihou.file.strategy.impl.AbstractFileStrategy; +import com.github.zuihou.utils.StrPool; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.FileInputStream; +import java.io.IOException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.UUID; + +import static com.github.zuihou.utils.DateUtils.DEFAULT_MONTH_FORMAT_SLASH; + +/** + * 阿里OSS + * + * @author zuihou + * @date 2019/08/09 + */ +@EnableConfigurationProperties(FileServerProperties.class) +@Configuration +@Slf4j +@ConditionalOnProperty(prefix = FileServerProperties.PREFIX, name = "type", havingValue = "ALI") +public class AliOssAutoConfigure { + + @Service + public class AliServiceImpl extends AbstractFileStrategy { + @Override + protected void uploadFile(File file, MultipartFile multipartFile) throws Exception { + FileServerProperties.Ali ali = fileProperties.getAli(); + OSS ossClient = new OSSClientBuilder().build(ali.getEndpoint(), ali.getAccessKeyId(), + ali.getAccessKeySecret()); + String bucketName = ali.getBucketName(); + if (!ossClient.doesBucketExist(bucketName)) { + ossClient.createBucket(bucketName); + } + + //生成文件名 + String fileName = StrUtil.join(StrPool.EMPTY, UUID.randomUUID().toString(), StrPool.DOT, file.getExt()); + //日期文件夹 + String tenant = BaseContextHandler.getTenant(); + String relativePath = tenant + StrPool.SLASH + LocalDate.now().format(DateTimeFormatter.ofPattern(DEFAULT_MONTH_FORMAT_SLASH)); + // web服务器存放的绝对路径 + String relativeFileName = relativePath + StrPool.SLASH + fileName; + + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentDisposition("attachment;fileName=" + file.getSubmittedFileName()); + metadata.setContentType(file.getContextType()); + PutObjectRequest request = new PutObjectRequest(bucketName, relativeFileName, multipartFile.getInputStream(), metadata); + PutObjectResult result = ossClient.putObject(request); + + log.info("result={}", JSONObject.toJSONString(result)); + + String url = ali.getUriPrefix() + relativeFileName; + file.setUrl(StrUtil.replace(url, "\\\\", StrPool.SLASH)); + file.setFilename(fileName); + file.setRelativePath(relativePath); + + file.setGroup(result.getETag()); + file.setPath(result.getRequestId()); + + ossClient.shutdown(); + } + + @Override + protected void delete(List list, FileDeleteDO file) { + FileServerProperties.Ali ali = fileProperties.getAli(); + String bucketName = ali.getBucketName(); + OSS ossClient = new OSSClientBuilder().build(ali.getEndpoint(), ali.getAccessKeyId(), + ali.getAccessKeySecret()); + ossClient.deleteObject(bucketName, file.getRelativePath() + StrPool.SLASH + file.getFileName()); + ossClient.shutdown(); + } + } + + + @Service + public class AliChunkServiceImpl extends AbstractFileChunkStrategy { + @Override + protected void copyFile(File file) { + FileServerProperties.Ali ali = fileProperties.getAli(); + String sourceBucketName = ali.getBucketName(); + String destinationBucketName = ali.getBucketName(); + OSS ossClient = new OSSClientBuilder().build(ali.getEndpoint(), ali.getAccessKeyId(), + ali.getAccessKeySecret()); + + String sourceObjectName = file.getRelativePath() + StrPool.SLASH + file.getFilename(); + String fileName = UUID.randomUUID().toString() + StrPool.DOT + file.getExt(); + String destinationObjectName = file.getRelativePath() + StrPool.SLASH + fileName; + ObjectMetadata objectMetadata = ossClient.getObjectMetadata(sourceBucketName, sourceObjectName); + // 获取被拷贝文件的大小。 + + // 获取被拷贝文件的大小。 + long contentLength = objectMetadata.getContentLength(); + + // 设置分片大小为10MB。 + long partSize = 1024 * 1024 * 10; + + // 计算分片总数。 + int partCount = (int) (contentLength / partSize); + if (contentLength % partSize != 0) { + partCount++; + } + log.info("total part count:{}", partCount); + + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentDisposition("attachment;fileName=" + file.getSubmittedFileName()); + metadata.setContentType(file.getContextType()); + // 初始化拷贝任务。可以通过InitiateMultipartUploadRequest指定目标文件元信息。 + InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(destinationBucketName, destinationObjectName, metadata); + InitiateMultipartUploadResult initiateMultipartUploadResult = ossClient.initiateMultipartUpload(initiateMultipartUploadRequest); + String uploadId = initiateMultipartUploadResult.getUploadId(); + + // 分片拷贝。 + List partETags = new ArrayList<>(); + for (int i = 0; i < partCount; i++) { + // 计算每个分片的大小。 + long skipBytes = partSize * i; + long size = partSize < contentLength - skipBytes ? partSize : contentLength - skipBytes; + + // 创建UploadPartCopyRequest。可以通过UploadPartCopyRequest指定限定条件。 + UploadPartCopyRequest uploadPartCopyRequest = + new UploadPartCopyRequest(sourceBucketName, sourceObjectName, destinationBucketName, destinationObjectName); + uploadPartCopyRequest.setUploadId(uploadId); + uploadPartCopyRequest.setPartSize(size); + uploadPartCopyRequest.setBeginIndex(skipBytes); + uploadPartCopyRequest.setPartNumber(i + 1); + UploadPartCopyResult uploadPartCopyResult = ossClient.uploadPartCopy(uploadPartCopyRequest); + + // 将返回的分片ETag保存到partETags中。 + partETags.add(uploadPartCopyResult.getPartETag()); + } + + // 提交分片拷贝任务。 + CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest( + destinationBucketName, destinationObjectName, uploadId, partETags); + ossClient.completeMultipartUpload(completeMultipartUploadRequest); + + String url = new StringBuilder(ali.getUriPrefix()) + .append(file.getRelativePath()) + .append(StrPool.SLASH) + .append(fileName) + .toString(); + file.setUrl(StringUtils.replace(url, "\\\\", StrPool.SLASH)); + file.setFilename(fileName); + + // 关闭OSSClient。 + ossClient.shutdown(); + } + + @Override + protected R merge(List files, String path, String fileName, FileChunksMergeDTO info) throws IOException { + FileServerProperties.Ali ali = fileProperties.getAli(); + String bucketName = ali.getBucketName(); + OSS ossClient = new OSSClientBuilder().build(ali.getEndpoint(), ali.getAccessKeyId(), + ali.getAccessKeySecret()); + + //日期文件夹 + String relativePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM")); + // web服务器存放的绝对路径 + String relativeFileName = relativePath + StrPool.SLASH + fileName; + + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentDisposition("attachment;fileName=" + info.getSubmittedFileName()); + metadata.setContentType(info.getContextType()); + //步骤1:初始化一个分片上传事件。 + InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, relativeFileName, metadata); + InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(request); + // 返回uploadId,它是分片上传事件的唯一标识,您可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。 + String uploadId = result.getUploadId(); + + // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。 + List partETags = new ArrayList(); + for (int i = 0; i < files.size(); i++) { + java.io.File file = files.get(i); + FileInputStream in = FileUtils.openInputStream(file); + + UploadPartRequest uploadPartRequest = new UploadPartRequest(); + uploadPartRequest.setBucketName(bucketName); + uploadPartRequest.setKey(relativeFileName); + uploadPartRequest.setUploadId(uploadId); + uploadPartRequest.setInputStream(in); + // 设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100KB。 + uploadPartRequest.setPartSize(file.length()); + // 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出这个范围,OSS将返回InvalidArgument的错误码。 + uploadPartRequest.setPartNumber(i + 1); + + // 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。 + UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest); + + // 每次上传分片之后,OSS的返回结果会包含一个PartETag。PartETag将被保存到partETags中。 + partETags.add(uploadPartResult.getPartETag()); + } + + /* 步骤3:完成分片上传。 */ + // 排序。partETags必须按分片号升序排列。 + partETags.sort(Comparator.comparingInt(PartETag::getPartNumber)); + + // 在执行该操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。 + CompleteMultipartUploadRequest completeMultipartUploadRequest = + new CompleteMultipartUploadRequest(bucketName, relativeFileName, uploadId, partETags); + + CompleteMultipartUploadResult uploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest); + + String url = new StringBuilder(ali.getUriPrefix()) + .append(relativePath) + .append(StrPool.SLASH) + .append(fileName) + .toString(); + File filePo = File.builder() + .relativePath(relativePath) + .group(uploadResult.getETag()) + .path(uploadResult.getRequestId()) + .url(StringUtils.replace(url, "\\\\", StrPool.SLASH)) + .build(); + + // 关闭OSSClient。 + ossClient.shutdown(); + return R.success(filePo); + } + } +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/storage/FastDfsAutoConfigure.java b/blade-service/blade-file/src/main/java/org/springblade/file/storage/FastDfsAutoConfigure.java new file mode 100644 index 0000000000000000000000000000000000000000..7bbc8c1e371c98d41d56d93f1d320abff8cfddb6 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/storage/FastDfsAutoConfigure.java @@ -0,0 +1,120 @@ +package com.github.zuihou.file.storage; + + +import com.github.tobato.fastdfs.domain.fdfs.StorePath; +import com.github.tobato.fastdfs.service.AppendFileStorageClient; +import com.github.tobato.fastdfs.service.FastFileStorageClient; +import com.github.zuihou.base.R; +import com.github.zuihou.file.dao.AttachmentMapper; +import com.github.zuihou.file.domain.FileDeleteDO; +import com.github.zuihou.file.dto.chunk.FileChunksMergeDTO; +import com.github.zuihou.file.entity.File; +import com.github.zuihou.file.properties.FileServerProperties; +import com.github.zuihou.file.strategy.impl.AbstractFileChunkStrategy; +import com.github.zuihou.file.strategy.impl.AbstractFileStrategy; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +/** + * FastDFS配置 + * + * @author zuihou + */ +@EnableConfigurationProperties(FileServerProperties.class) +@Configuration +@Slf4j +@ConditionalOnProperty(prefix = FileServerProperties.PREFIX, name = "type", havingValue = "FAST_DFS") +public class FastDfsAutoConfigure { + @Service + + public class FastDfsServiceImpl extends AbstractFileStrategy { + @Autowired + private FastFileStorageClient storageClient; + @Autowired + private AttachmentMapper attachmentMapper; + + @Override + protected void uploadFile(File file, MultipartFile multipartFile) throws Exception { + StorePath storePath = storageClient.uploadFile(multipartFile.getInputStream(), multipartFile.getSize(), file.getExt(), null); + file.setUrl(fileProperties.getUriPrefix() + storePath.getFullPath()); + file.setGroup(storePath.getGroup()); + file.setPath(storePath.getPath()); + } + + @Override + protected void delete(List list, FileDeleteDO file) { + if (file.getFile()) { + List ids = list.stream().mapToLong(FileDeleteDO::getId).boxed().collect(Collectors.toList()); + Integer count = attachmentMapper.countByGroup(ids, file.getGroup(), file.getPath()); + if (count > 0) { + return; + } + } + storageClient.deleteFile(file.getGroup(), file.getPath()); + } + + } + + @Service + public class FastDfsChunkServiceImpl extends AbstractFileChunkStrategy { + @Autowired + protected AppendFileStorageClient storageClient; + + @Override + protected void copyFile(File file) { + // 由于大文件下载然后在上传会内存溢出, 所以 FastDFS 不复制,删除时通过业务手段 +// DownloadByteArray callback = new DownloadByteArray(); +// byte[] content = storageClient.downloadFile(file.getGroup(), file.getPath(), callback); +// InputStream in = new ByteArrayInputStream(content); +// StorePath storePath = storageClient.uploadFile(file.getGroup(), in, file.getSize(), file.getExt()); +// file.setUrl(fileProperties.getUriPrefix() + storePath.getFullPath()); +// file.setGroup(storePath.getGroup()); +// file.setPath(storePath.getPath()); + } + + @Override + protected R merge(List files, String path, String fileName, FileChunksMergeDTO info) throws IOException { + StorePath storePath = null; + + long start = System.currentTimeMillis(); + for (int i = 0; i < files.size(); i++) { + java.io.File file = files.get(i); + + FileInputStream in = FileUtils.openInputStream(file); + if (i == 0) { + storePath = storageClient.uploadAppenderFile(null, in, + file.length(), info.getExt()); + } else { + storageClient.appendFile(storePath.getGroup(), storePath.getPath(), + in, file.length()); + } + } + if (storePath == null) { + return R.fail("上传失败"); + } + + long end = System.currentTimeMillis(); + log.info("上传耗时={}", (end - start)); + String url = new StringBuilder(fileProperties.getUriPrefix()) + .append(storePath.getFullPath()) + .toString(); + File filePo = File.builder() + .url(url) + .group(storePath.getGroup()) + .path(storePath.getPath()) + .build(); + return R.success(filePo); + } + } +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/storage/LocalAutoConfigure.java b/blade-service/blade-file/src/main/java/org/springblade/file/storage/LocalAutoConfigure.java new file mode 100644 index 0000000000000000000000000000000000000000..ca6dd1b1a0c01341b7f1f6caedf69a9489640617 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/storage/LocalAutoConfigure.java @@ -0,0 +1,171 @@ +package com.github.zuihou.file.storage; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.util.StrUtil; +import com.github.zuihou.base.R; +import com.github.zuihou.context.BaseContextHandler; +import com.github.zuihou.exception.BizException; +import com.github.zuihou.file.domain.FileDeleteDO; +import com.github.zuihou.file.dto.chunk.FileChunksMergeDTO; +import com.github.zuihou.file.entity.File; +import com.github.zuihou.file.properties.FileServerProperties; +import com.github.zuihou.file.strategy.impl.AbstractFileChunkStrategy; +import com.github.zuihou.file.strategy.impl.AbstractFileStrategy; +import com.github.zuihou.file.utils.FileDataTypeUtil; +import com.github.zuihou.utils.StrPool; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.UUID; + +import static com.github.zuihou.utils.DateUtils.DEFAULT_MONTH_FORMAT_SLASH; + + +/** + * 本地上传配置 + * + * @author zuihou + * @date 2019/06/18 + */ + +@EnableConfigurationProperties(FileServerProperties.class) +@Configuration +@ConditionalOnProperty(prefix = FileServerProperties.PREFIX, name = "type", havingValue = "LOCAL", matchIfMissing = true) +@Slf4j +public class LocalAutoConfigure { + + @Service + public class LocalServiceImpl extends AbstractFileStrategy { + @Override + protected void uploadFile(File file, MultipartFile multipartFile) throws Exception { + //生成文件名 + String fileName = UUID.randomUUID().toString() + StrPool.DOT + file.getExt(); + + String tenant = BaseContextHandler.getTenant(); + + //日期文件夹 + String relativePath = Paths.get(tenant, LocalDate.now().format(DateTimeFormatter.ofPattern(DEFAULT_MONTH_FORMAT_SLASH))).toString(); + // web服务器存放的绝对路径 + String absolutePath = Paths.get(fileProperties.getStoragePath(), relativePath).toString(); + + java.io.File outFile = new java.io.File(Paths.get(absolutePath, fileName).toString()); + org.apache.commons.io.FileUtils.writeByteArrayToFile(outFile, multipartFile.getBytes()); + + String url = new StringBuilder(fileProperties.getUriPrefix()) + .append(relativePath) + .append(StrPool.SLASH) + .append(fileName) + .toString(); + //替换掉windows环境的\路径 + url = StrUtil.replace(url, "\\\\", StrPool.SLASH); + url = StrUtil.replace(url, "\\", StrPool.SLASH); + file.setUrl(url); + file.setFilename(fileName); + file.setRelativePath(relativePath); + } + + @Override + protected void delete(List list, FileDeleteDO file) { + java.io.File ioFile = new java.io.File(Paths.get(fileProperties.getStoragePath(), file.getRelativePath(), file.getFileName()).toString()); + org.apache.commons.io.FileUtils.deleteQuietly(ioFile); + } + } + + @Service + public class LocalChunkServiceImpl extends AbstractFileChunkStrategy { + /** + * 为上传的文件生成随机名称 + * + * @param originalName 文件的原始名称,主要用来获取文件的后缀名 + * @return + */ + private String randomFileName(String originalName) { + String[] ext = StrUtil.split(originalName, "."); + return UUID.randomUUID().toString() + StrPool.DOT + ext[ext.length - 1]; + } + + @Override + protected void copyFile(File file) { + String inputFile = Paths.get(fileProperties.getStoragePath(), file.getRelativePath(), + file.getFilename()).toString(); + + String filename = randomFileName(file.getFilename()); + String outputFile = Paths.get(fileProperties.getStoragePath(), file.getRelativePath(), filename).toString(); + + try { + FileUtil.copy(inputFile, outputFile, true); + } catch (IORuntimeException e) { + log.error("复制文件异常", e); + throw new BizException("复制文件异常"); + } + + file.setFilename(filename); + String url = file.getUrl(); + String newUrl = StrUtil.subPre(url, StrUtil.lastIndexOfIgnoreCase(url, StrPool.SLASH) + 1); + file.setUrl(newUrl + filename); + } + + + @Override + protected R merge(List files, String path, String fileName, FileChunksMergeDTO info) throws IOException { + //创建合并后的文件 + log.info("path={},fileName={}", path, fileName); + java.io.File outputFile = new java.io.File(Paths.get(path, fileName).toString()); + if (!outputFile.exists()) { + boolean newFile = outputFile.createNewFile(); + if (!newFile) { + return R.fail("创建文件失败"); + } + try (FileChannel outChannel = new FileOutputStream(outputFile).getChannel()) { + //同步nio 方式对分片进行合并, 有效的避免文件过大导致内存溢出 + for (java.io.File file : files) { + try (FileChannel inChannel = new FileInputStream(file).getChannel()) { + inChannel.transferTo(0, inChannel.size(), outChannel); + } catch (FileNotFoundException ex) { + log.error("文件转换失败", ex); + return R.fail("文件转换失败"); + } + //删除分片 + if (!file.delete()) { + log.error("分片[" + info.getName() + "=>" + file.getName() + "]删除失败"); + } + } + } catch (FileNotFoundException e) { + log.error("文件输出失败", e); + return R.fail("文件输出失败"); + } + + } else { + log.warn("文件[{}], fileName={}已经存在", info.getName(), fileName); + } + + String relativePath = FileDataTypeUtil.getRelativePath(Paths.get(fileProperties.getStoragePath()).toString(), outputFile.getAbsolutePath()); + log.info("relativePath={}, getStoragePath={}, getAbsolutePath={}", relativePath, fileProperties.getStoragePath(), outputFile.getAbsolutePath()); + String url = new StringBuilder(fileProperties.getUriPrefix()) + .append(relativePath) + .append(StrPool.SLASH) + .append(fileName) + .toString(); + File filePo = File.builder() + .relativePath(relativePath) + .url(StringUtils.replace(url, "\\\\", StrPool.SLASH)) + .build(); + return R.success(filePo); + } + } +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/strategy/FileChunkStrategy.java b/blade-service/blade-file/src/main/java/org/springblade/file/strategy/FileChunkStrategy.java new file mode 100644 index 0000000000000000000000000000000000000000..5f283b5110f244e780f952ed38d0c578e1278761 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/strategy/FileChunkStrategy.java @@ -0,0 +1,33 @@ +package com.github.zuihou.file.strategy; + + +import com.github.zuihou.base.R; +import com.github.zuihou.file.dto.chunk.FileChunksMergeDTO; +import com.github.zuihou.file.entity.File; + +/** + * 文件分片处理策略类 + * + * @author zuihou + * @date 2019/06/19 + */ +public interface FileChunkStrategy { + + /** + * 根据md5检测文件 + * + * @param md5 + * @param folderId + * @param accountId + * @return + */ + File md5Check(String md5, Long folderId, Long accountId); + + /** + * 合并文件 + * + * @param merge + * @return + */ + R chunksMerge(FileChunksMergeDTO merge); +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/strategy/FileLock.java b/blade-service/blade-file/src/main/java/org/springblade/file/strategy/FileLock.java new file mode 100644 index 0000000000000000000000000000000000000000..189489a9216aac327a56b4be38cef647c29ae744 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/strategy/FileLock.java @@ -0,0 +1,50 @@ +package com.github.zuihou.file.strategy; + +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 文件锁工具类 + * + * @author zuihou + * @date 2019-06-14 + */ +@Component +public class FileLock { + + private static Map LOCKS = new HashMap(); + + /** + * 获取锁 + * + * @param key + * @return java.util.concurrent.locks.Lock + * @author zuihou + * @date 2019-06-14 11:30 + */ + public static synchronized Lock getLock(String key) { + if (LOCKS.containsKey(key)) { + return LOCKS.get(key); + } else { + Lock one = new ReentrantLock(); + LOCKS.put(key, one); + return one; + } + } + + /** + * 删除锁 + * + * @param key + * @return void + * @author zuihou + * @date 2019-06-14 11:33 + */ + public static synchronized void removeLock(String key) { + LOCKS.remove(key); + } +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/strategy/FileStrategy.java b/blade-service/blade-file/src/main/java/org/springblade/file/strategy/FileStrategy.java new file mode 100644 index 0000000000000000000000000000000000000000..da0a2c79e944b4ee44b979ac59ce7538082c5ccb --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/strategy/FileStrategy.java @@ -0,0 +1,36 @@ +package com.github.zuihou.file.strategy; + +import com.github.zuihou.file.domain.FileDeleteDO; +import com.github.zuihou.file.entity.File; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * 文件策略接口 + * + * @author zuihou + * @date 2019/06/17 + */ +public interface FileStrategy { + /** + * 文件上传 + * + * @param file 文件 + * @return 文件对象 + * @author zuihou + * @date 2019-05-06 16:38 + */ + File upload(MultipartFile file); + + /** + * 删除源文件 + * + * @param list 列表 + * @return + * @author zuihou + * @date 2019-05-07 11:41 + */ + boolean delete(List list); + +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/strategy/impl/AbstractFileChunkStrategy.java b/blade-service/blade-file/src/main/java/org/springblade/file/strategy/impl/AbstractFileChunkStrategy.java new file mode 100644 index 0000000000000000000000000000000000000000..607a02604b62fe64b0775d6e70025ec030bdfef7 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/strategy/impl/AbstractFileChunkStrategy.java @@ -0,0 +1,251 @@ +package com.github.zuihou.file.strategy.impl; + +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.github.zuihou.base.R; +import com.github.zuihou.file.domain.FileAttrDO; +import com.github.zuihou.file.dto.chunk.FileChunksMergeDTO; +import com.github.zuihou.file.entity.File; +import com.github.zuihou.file.enumeration.IconType; +import com.github.zuihou.file.properties.FileServerProperties; +import com.github.zuihou.file.service.FileService; +import com.github.zuihou.file.strategy.FileChunkStrategy; +import com.github.zuihou.file.strategy.FileLock; +import com.github.zuihou.file.utils.FileDataTypeUtil; +import com.github.zuihou.utils.DateUtils; +import com.github.zuihou.utils.StrPool; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.IOException; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.locks.Lock; + + +/** + * 文件分片处理 抽象策略类 + * + * @author zuihou + * @date 2019/06/19 + */ +@Slf4j +public abstract class AbstractFileChunkStrategy implements FileChunkStrategy { + @Autowired + protected FileService fileService; + @Autowired + protected FileServerProperties fileProperties; + + /** + * 秒传验证 + * 根据文件的MD5签名判断该文件是否已经存在 + * + * @param md5 文件的md5签名 + * @return 若存在则返回该文件的路径,不存在则返回null + */ + private File md5Check(String md5) { + return fileService.getOne(Wrappers.lambdaQuery() + .eq(File::getFileMd5, md5).eq(File::getIsDelete, false), false); + } + + /** + * 设置创建日期 + * + * @param file + */ + private void setDate(File file) { + LocalDateTime now = LocalDateTime.now(); + file.setCreateMonth(DateUtils.formatAsYearMonthEn(now)) + .setCreateWeek(DateUtils.formatAsYearWeekEn(now)) + .setCreateDay(DateUtils.formatAsDateEn(now)); + } + + @Override + public File md5Check(String md5, Long folderId, Long accountId) { + File file = md5Check(md5); + if (file == null) { + return null; + } + + //分片存在,不需上传, 复制一条数据,重新插入 + copyFile(file); + + file.setId(null) + .setCreateUser(accountId) + .setCreateTime(LocalDateTime.now()); + file.setUpdateTime(LocalDateTime.now()) + .setUpdateUser(accountId); + setDate(file); + + FileAttrDO attr = fileService.getFileAttrDo(folderId); + file.setFolderId(folderId) + .setTreePath(attr.getTreePath()) + .setGrade(attr.getGrade()) + .setFolderName(attr.getFolderName()); + + fileService.save(file); + return file; + } + + /** + * 让子类自己实现复制 + * + * @param file + */ + protected abstract void copyFile(File file); + + @Override + public R chunksMerge(FileChunksMergeDTO info) { + + + String filename = new StringBuilder(info.getName()) + .append(StrPool.DOT) + .append(info.getExt()).toString(); + R result = chunksMerge(info, filename); + + log.info("path={}", result); + if (result.getIsSuccess() && result.getData() != null) { + //文件名 + File filePo = result.getData(); + + filePo.setDataType(FileDataTypeUtil.getDataType(info.getContextType())) + .setSubmittedFileName(info.getSubmittedFileName()) + .setIsDelete(false) + .setSize(info.getSize()) + .setFileMd5(info.getMd5()) + .setContextType(info.getContextType()) + .setFilename(filename) + .setExt(info.getExt()) + .setIcon(IconType.getIcon(info.getExt()).getIcon()); + setDate(filePo); + + FileAttrDO attr = fileService.getFileAttrDo(info.getFolderId()); + filePo.setTreePath(attr.getTreePath()) + .setGrade(attr.getGrade()) + .setFolderId(info.getFolderId()) + .setFolderName(attr.getFolderName()); + + fileService.save(filePo); + return R.success(filePo); + } + return result; + } + + private R chunksMerge(FileChunksMergeDTO info, String fileName) { + String path = FileDataTypeUtil.getUploadPathPrefix(fileProperties.getStoragePath()); + int chunks = info.getChunks(); + String folder = info.getName(); + String md5 = info.getMd5(); + + int chunksNum = this.getChunksNum(Paths.get(path, folder).toString()); + log.info("chunks={}, chunksNum={}", chunks, chunksNum); + //检查是否满足合并条件:分片数量是否足够 + if (chunks == chunksNum) { + //同步指定合并的对象 + Lock lock = FileLock.getLock(folder); + try { + lock.lock(); + //检查是否满足合并条件:分片数量是否足够 + List files = new ArrayList<>(Arrays.asList(this.getChunks(Paths.get(path, folder).toString()))); + if (chunks == files.size()) { + //按照名称排序文件,这里分片都是按照数字命名的 + + //这里存放的文件名一定是数字 + files.sort(Comparator.comparingInt(f -> Convert.toInt(f.getName(), 0))); + + R result = merge(files, path, fileName, info); + files = null; + + //清理:文件夹,tmp文件 + this.cleanSpace(folder, path); + return result; + } + } catch (Exception ex) { + log.error("数据分片合并失败", ex); + return R.fail("数据分片合并失败"); + } finally { + //解锁 + lock.unlock(); + //清理锁对象 + FileLock.removeLock(folder); + } + } + //去持久层查找对应md5签名,直接返回对应path + File file = this.md5Check(md5); + if (file == null) { + log.error("文件[签名:" + md5 + "]数据不完整,可能该文件正在合并中"); + return R.fail("数据不完整,可能该文件正在合并中, 也有可能是上传过程中某些分片丢失"); + } + return R.success(file); + } + + + /** + * 子类实现具体的合并操作 + * + * @param files 文件 + * @param path 路径 + * @param fileName 唯一名 含后缀 + * @param info 文件信息 + * @return + * @throws IOException + */ + protected abstract R merge(List files, String path, String fileName, FileChunksMergeDTO info) throws IOException; + + + /** + * 清理分片上传的相关数据 + * 文件夹,tmp文件 + * + * @param folder 文件夹名称 + * @param path 上传文件根路径 + * @return + */ + protected boolean cleanSpace(String folder, String path) { + //删除分片文件夹 + java.io.File garbage = new java.io.File(Paths.get(path, folder).toString()); + if (!FileUtils.deleteQuietly(garbage)) { + return false; + } + //删除tmp文件 + garbage = new java.io.File(Paths.get(path, folder + ".tmp").toString()); + if (!FileUtils.deleteQuietly(garbage)) { + return false; + } + return true; + } + + + /** + * 获取指定文件的分片数量 + * + * @param folder 文件夹路径 + * @return + */ + private int getChunksNum(String folder) { + java.io.File[] filesList = this.getChunks(folder); + return filesList.length; + } + + /** + * 获取指定文件的所有分片 + * + * @param folder 文件夹路径 + * @return + */ + private java.io.File[] getChunks(String folder) { + java.io.File targetFolder = new java.io.File(folder); + return targetFolder.listFiles((file) -> { + if (file.isDirectory()) { + return false; + } + return true; + }); + } + +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/strategy/impl/AbstractFileStrategy.java b/blade-service/blade-file/src/main/java/org/springblade/file/strategy/impl/AbstractFileStrategy.java new file mode 100644 index 0000000000000000000000000000000000000000..c04d7aabbf5f68e7deca657b50c143a440b8fec5 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/strategy/impl/AbstractFileStrategy.java @@ -0,0 +1,108 @@ +package com.github.zuihou.file.strategy.impl; + +import com.github.zuihou.exception.BizException; +import com.github.zuihou.file.domain.FileDeleteDO; +import com.github.zuihou.file.entity.File; +import com.github.zuihou.file.enumeration.IconType; +import com.github.zuihou.file.properties.FileServerProperties; +import com.github.zuihou.file.strategy.FileStrategy; +import com.github.zuihou.file.utils.FileDataTypeUtil; +import com.github.zuihou.utils.DateUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.multipart.MultipartFile; + +import java.time.LocalDateTime; +import java.util.List; + +import static com.github.zuihou.exception.code.ExceptionCode.BASE_VALID_PARAM; + + +/** + * 文件抽象策略 处理类 + * + * @author zuihou + * @date 2019/06/17 + */ +@Slf4j +public abstract class AbstractFileStrategy implements FileStrategy { + + private static final String FILE_SPLIT = "."; + @Autowired + protected FileServerProperties fileProperties; + + /** + * 上传文件 + * + * @param multipartFile + * @return + */ + @Override + public File upload(MultipartFile multipartFile) { + try { + if (!multipartFile.getOriginalFilename().contains(FILE_SPLIT)) { + throw BizException.wrap(BASE_VALID_PARAM.build("缺少后缀名")); + } + + File file = File.builder() + .isDelete(false).submittedFileName(multipartFile.getOriginalFilename()) + .contextType(multipartFile.getContentType()) + .dataType(FileDataTypeUtil.getDataType(multipartFile.getContentType())) + .size(multipartFile.getSize()) + .ext(FilenameUtils.getExtension(multipartFile.getOriginalFilename())) + .build(); + file.setIcon(IconType.getIcon(file.getExt()).getIcon()); + setDate(file); + uploadFile(file, multipartFile); + return file; + } catch (Exception e) { + log.error("e={}", e); + throw BizException.wrap(BASE_VALID_PARAM.build("文件上传失败")); + } + } + + /** + * 具体类型执行上传操作 + * + * @param file + * @param multipartFile + * @throws Exception + */ + protected abstract void uploadFile(File file, MultipartFile multipartFile) throws Exception; + + private void setDate(File file) { + LocalDateTime now = LocalDateTime.now(); + file.setCreateMonth(DateUtils.formatAsYearMonthEn(now)) + .setCreateWeek(DateUtils.formatAsYearWeekEn(now)) + .setCreateDay(DateUtils.formatAsDateEn(now)); + } + + @Override + public boolean delete(List list) { + if (list.isEmpty()) { + return true; + } + boolean flag = false; + for (FileDeleteDO file : list) { + try { + delete(list, file); + flag = true; + } catch (Exception e) { + log.error("删除文件失败", e); + } + } + return flag; + } + + /** + * 具体执行删除方法, 无需处理异常 + * + * @param list + * @param file + * @author zuihou + * @date 2019-05-07 + */ + protected abstract void delete(List list, FileDeleteDO file); + +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/utils/FileDataTypeUtil.java b/blade-service/blade-file/src/main/java/org/springblade/file/utils/FileDataTypeUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..a55f735b14aab87deedcf9efa40e5896628a9eae --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/utils/FileDataTypeUtil.java @@ -0,0 +1,82 @@ +package com.github.zuihou.file.utils; + + +import com.github.zuihou.file.enumeration.DataType; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +/** + * 根据类型识别工具 + * + * @author zuihou + * @date 2019-05-06 + */ +@Slf4j +public class FileDataTypeUtil { + final static DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy/MM"); + private final static String IMAGE = "image"; + private final static String VIDEO = "video"; + private final static String DIR = "application/x-director"; + private final static String AUDIO = "audio"; + private final static String TEXT = "text"; + + /** + * 根据mine类型,返回文件类型 + * + * @param + * @return + * @author zuihou + * @date 2019-05-06 13:41 + */ + public static DataType getDataType(String mime) { + if (mime == null || "".equals(mime)) { + return DataType.OTHER; + } + if (mime.contains(IMAGE)) { + return DataType.IMAGE; + } else if (mime.contains(TEXT) + || mime.startsWith("application/vnd.ms-excel") + || mime.startsWith("application/msword") + || mime.startsWith("application/pdf") + || mime.startsWith("application/vnd.ms-project") + || mime.startsWith("application/vnd.ms-works") + || mime.startsWith("application/x-javascript") + || mime.startsWith("application/vnd.openxmlformats-officedocument") + || mime.startsWith("application/vnd.ms-word.document.macroEnabled") + || mime.startsWith("application/vnd.ms-word.template.macroEnabled") + || mime.startsWith("application/vnd.ms-powerpoint") + ) { + return DataType.DOC; + } else if (mime.contains(VIDEO)) { + return DataType.VIDEO; + } else if (mime.contains(DIR)) { + return DataType.DIR; + } else if (mime.contains(AUDIO)) { + return DataType.AUDIO; + } else { + return DataType.OTHER; + } + } + + public static String getUploadPathPrefix(String uploadPathPrefix) { + //日期文件夹 + String secDir = LocalDate.now().format(DTF); + // web服务器存放的绝对路径 + String absolutePath = Paths.get(uploadPathPrefix, secDir).toString(); + return absolutePath; + } + + public static String getRelativePath(String pathPrefix, String path) { + String remove = StringUtils.remove(path, pathPrefix + File.separator); + + log.info("remove={}, index={}", remove, remove.lastIndexOf(java.io.File.separator)); + String relativePath = StringUtils.substring(remove, 0, remove.lastIndexOf(java.io.File.separator)); + return relativePath; + } + +} diff --git a/blade-service/blade-file/src/main/java/org/springblade/file/utils/ZipUtils.java b/blade-service/blade-file/src/main/java/org/springblade/file/utils/ZipUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..2e3c6eb0b6902b84e17d86b10f95ef90925dc1d1 --- /dev/null +++ b/blade-service/blade-file/src/main/java/org/springblade/file/utils/ZipUtils.java @@ -0,0 +1,254 @@ +package com.github.zuihou.file.utils; + + +import com.github.zuihou.exception.BizException; +import com.github.zuihou.utils.StrPool; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.Enumeration; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +import static com.github.zuihou.utils.StrPool.SLASH; + + +/** + * ZipUtils on spring-boot-filemanager + * + * @author Alex Yang + * @date 2016年08月25日 10:08 + */ +@Slf4j +public class ZipUtils { + + private final static String AGENT_FIREFOX = "firefox"; + + private static void zipFiles(ZipOutputStream out, String path, File... srcFiles) { + path = path.replaceAll("\\*", SLASH); + if (!path.endsWith(SLASH)) { + path += SLASH; + } + byte[] buf = new byte[1024]; + try { + for (File srcFile : srcFiles) { + if (srcFile.isDirectory()) { + File[] files = srcFile.listFiles(); + String srcPath = srcFile.getName(); + srcPath = srcPath.replaceAll("\\*", SLASH); + if (!srcPath.endsWith(SLASH)) { + srcPath += SLASH; + } + out.putNextEntry(new ZipEntry(path + srcPath)); + zipFiles(out, path + srcPath, files); + } else { + try (FileInputStream in = new FileInputStream(srcFile)) { + out.putNextEntry(new ZipEntry(path + srcFile.getName())); + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + out.closeEntry(); + } + } + } + } catch (Exception e) { + log.info("ZipUtils error {} ", e); + } + } + + /** + * 通过流打包下载文件 + * + * @param out + * @param fileName + * @param + */ + public static void zipFilesByInputStream(ZipOutputStream out, String fileName, InputStream is) throws Exception { + byte[] buf = new byte[1024]; + try { + out.putNextEntry(new ZipEntry(fileName)); + int len; + while ((len = is.read(buf)) > 0) { + out.write(buf, 0, len); + } + is.close(); + } catch (Exception e) { + throw e; + } finally { + if (is != null) { + is.close(); + } + } + } + + /** + * 下载指定输入流的图片 + * + * @param + * @param + * @param + * @throws Exception + */ + private static void downloadFile(InputStream is, OutputStream out) throws Exception { + try { + byte[] b = new byte[2048]; + int length; + while ((length = is.read(b)) > 0) { + out.write(b, 0, length); + } + } catch (Exception e) { + throw e; + } finally { + if (out != null) { + out.close(); + } + if (is != null) { + is.close(); + } + } + } + + public static void unZipFiles(File zipFile, String descDir) throws IOException { + if (!descDir.endsWith(SLASH)) { + descDir += SLASH; + } + File pathFile = new File(descDir); + if (!pathFile.exists()) { + pathFile.mkdirs(); + } + try (ZipFile zip = new ZipFile(zipFile)) { + for (Enumeration entries = zip.entries(); entries.hasMoreElements(); ) { + ZipEntry entry = (ZipEntry) entries.nextElement(); + String zipEntryName = entry.getName(); + + String outPath = (descDir + zipEntryName).replaceAll("\\*", SLASH); + //判断路径是否存在,不存在则创建文件路径 + File file = new File(outPath.substring(0, outPath.lastIndexOf('/'))); + if (!file.exists()) { + file.mkdirs(); + } + //判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压 + if (new File(outPath).isDirectory()) { + continue; + } + + + try (InputStream in = zip.getInputStream(entry); OutputStream out = new FileOutputStream(outPath)) { + byte[] buf1 = new byte[1024]; + int len; + while ((len = in.read(buf1)) > 0) { + out.write(buf1, 0, len); + } + } + } + } catch (ZipException e) { + throw e; + } + } + + public static void zipFilesByInputStream(Map fileMap, Long fileSize, String extName, HttpServletRequest request, HttpServletResponse response) throws Exception { + HttpURLConnection connection = null; + + response.setContentType("application/octet-stream; charset=utf-8"); + String downloadFileName; + String agent = request.getHeader("USER-AGENT"); + if (agent != null && agent.toLowerCase().indexOf(AGENT_FIREFOX) > 0) { + downloadFileName = "=?UTF-8?B?" + (new String(Base64.encodeBase64((extName).getBytes("UTF-8")))) + "?="; + } else { + //~ \ / |:"<>? 这些字符不能被替换,因为系统允许文件名有这些字符!! + downloadFileName = URLEncoder.encode(extName, "UTF-8") + .replaceAll("\\+", "%20").replaceAll("%28", "\\(") + .replaceAll("%29", "\\)") + .replaceAll("%3B", StrPool.SEMICOLON) + .replaceAll("%40", StrPool.AT).replaceAll("%23", "\\#") + .replaceAll("%26", "\\&").replaceAll("%2C", "\\,") + .replaceAll("%2B", StrPool.PLUS).replaceAll("%25", StrPool.PERCENT) + .replaceAll("%21", StrPool.EXCLAMATION_MARK).replaceAll("%5E", StrPool.HAT) + .replaceAll("%24", "\\$").replaceAll("%7E", StrPool.TILDA) + + .replaceAll("%60", StrPool.BACKTICK).replaceAll("%5B", StrPool.LEFT_SQ_BRACKET) + .replaceAll("%3D", StrPool.EQUALS) + .replaceAll("%5D", StrPool.RIGHT_SQ_BRACKET).replaceAll("%5C", "\\\\") + .replaceAll("%27", StrPool.SINGLE_QUOTE).replaceAll("%2F", SLASH) + .replaceAll("%7B", StrPool.LEFT_BRACE).replaceAll("%7D", StrPool.RIGHT_BRACE) + + .replaceAll("%7C", "\\|").replaceAll("%3A", "\\:") + .replaceAll("%22", "\\\"").replaceAll("%3C", "\\<") + .replaceAll("%3E", "\\>").replaceAll("%3F", "\\?") + + ; + log.info("downloadFileName={}", downloadFileName); + } + response.setHeader("Content-Disposition", "attachment;fileName=" + downloadFileName); + if (fileSize != null && fileSize > 0) { + // 加了这个下载会报错? +// response.setHeader("Content-Length", String.valueOf(fileSize)); + } + + ServletOutputStream out = response.getOutputStream(); + if (fileMap.size() == 1) { + String url = null; + for (Map.Entry entry : fileMap.entrySet()) { + url = entry.getValue(); + } + try { + connection = getConnection(url); + ZipUtils.downloadFile(connection.getInputStream(), out); + } catch (Exception e) { + throw new BizException("文件地址连接超时"); + } + return; + } + + try (ZipOutputStream zos = new ZipOutputStream(out); BufferedOutputStream bos = new BufferedOutputStream(zos)) { + for (Map.Entry entry : fileMap.entrySet()) { + String fileName = entry.getKey(); + String url = entry.getValue(); + BufferedInputStream bis = null; + try { + connection = getConnection(url); + bis = new BufferedInputStream(connection.getInputStream()); + zos.putNextEntry(new ZipEntry(fileName)); + + int len; + byte[] buf = new byte[10 * 1024]; + while ((len = bis.read(buf, 0, buf.length)) != -1) { + bos.write(buf, 0, len); + } + bos.flush(); + } catch (Exception e) { + log.warn("打包下载多个文件异常, fileName=" + fileName + ",url=" + url, e); + } finally { + if (connection != null) { + connection.disconnect(); + } + if (bis != null) { + bis.close(); + } + if (zos != null) { + zos.closeEntry(); + } + } + } + } + } + + private static HttpURLConnection getConnection(String url) throws Exception { + log.info("url={}", url); + URL conURL = new URL(url); + HttpURLConnection connection = (HttpURLConnection) conURL.openConnection(); + connection.connect(); + return connection; + } +} diff --git a/blade-service/blade-file/src/main/resources/application-dev.yml b/blade-service/blade-file/src/main/resources/application-dev.yml new file mode 100644 index 0000000000000000000000000000000000000000..e79c2ca82b2e75d0192b65354baae254c3947487 --- /dev/null +++ b/blade-service/blade-file/src/main/resources/application-dev.yml @@ -0,0 +1,10 @@ +#服务器端口 +server: + port: 8203 + +#数据源配置 +spring: + datasource: + url: ${blade.datasource.dev.url} + username: ${blade.datasource.dev.username} + password: ${blade.datasource.dev.password} diff --git a/blade-service/blade-file/src/main/resources/application-prod.yml b/blade-service/blade-file/src/main/resources/application-prod.yml new file mode 100644 index 0000000000000000000000000000000000000000..c0d8c0562a0cadfa783ac1f4e242e8e8eff1441f --- /dev/null +++ b/blade-service/blade-file/src/main/resources/application-prod.yml @@ -0,0 +1,10 @@ +#服务器端口 +server: + port: 8102 + +#数据源配置 +spring: + datasource: + url: ${blade.datasource.prod.url} + username: ${blade.datasource.prod.username} + password: ${blade.datasource.prod.password} diff --git a/blade-service/blade-file/src/main/resources/application-test.yml b/blade-service/blade-file/src/main/resources/application-test.yml new file mode 100644 index 0000000000000000000000000000000000000000..57b842e0f9c228ee53a53e72933c6784f2a3b674 --- /dev/null +++ b/blade-service/blade-file/src/main/resources/application-test.yml @@ -0,0 +1,10 @@ +#服务器端口 +server: + port: 8102 + +#数据源配置 +spring: + datasource: + url: ${blade.datasource.test.url} + username: ${blade.datasource.test.username} + password: ${blade.datasource.test.password} diff --git a/blade-service/blade-file/src/main/resources/banner.txt b/blade-service/blade-file/src/main/resources/banner.txt new file mode 100644 index 0000000000000000000000000000000000000000..6b82083c89b24b21824d86ab7599bc5e8c4c057e --- /dev/null +++ b/blade-service/blade-file/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +${AnsiColor.BRIGHT_YELLOW} + .__.__ .___ .__ .__ .___ +__________ __|__| |__ ____ __ __ _____ __| _/_____ |__| ____ ____ | | ____ __ __ __| _/ +\___ / | \ | | \ / _ \| | \ ______ \__ \ / __ |/ \| |/ \ ______ _/ ___\| | / _ \| | \/ __ | + / /| | / | Y ( <_> ) | / /_____/ / __ \_/ /_/ | Y Y \ | | \ /_____/ \ \___| |_( <_> ) | / /_/ | +/_____ \____/|__|___| /\____/|____/ (____ /\____ |__|_| /__|___| / \___ >____/\____/|____/\____ | + \/ \/ \/ \/ \/ \/ \/ \/ +Application Version: @project.description@ - @project.version@ +Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version} +${AnsiColor.DEFAULT} diff --git a/blade-service/blade-file/src/main/resources/bootstrap.yml b/blade-service/blade-file/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..061bd68dbc3472c291cd6479c2bd6be76bbfb9b3 --- /dev/null +++ b/blade-service/blade-file/src/main/resources/bootstrap.yml @@ -0,0 +1,56 @@ +zuihou: + nacos: + ip: ${NACOS_IP:@nacos.ip@} + port: ${NACOS_PORT:@nacos.port@} + namespace: ${NACOS_ID:@nacos.namespace@} + username: ${NACOS_USERNAME:@nacos.username@} + password: ${NACOS_PASSWORD:@nacos.password@} +spring: + main: + allow-bean-definition-overriding: true + application: + name: @project.artifactId@ + profiles: + active: @profile.active@ + cloud: + nacos: + config: + server-addr: ${zuihou.nacos.ip}:${zuihou.nacos.port} + file-extension: yml + namespace: ${zuihou.nacos.namespace} + shared-configs: + - dataId: common.yml + refresh: true + - dataId: redis.yml + refresh: false + - dataId: mysql.yml + refresh: true + - dataId: rabbitmq.yml + refresh: false + enabled: true + username: ${zuihou.nacos.username} + password: ${zuihou.nacos.password} + discovery: + username: ${zuihou.nacos.username} + password: ${zuihou.nacos.password} + server-addr: ${zuihou.nacos.ip}:${zuihou.nacos.port} + namespace: ${zuihou.nacos.namespace} + metadata: # 元数据,用于权限服务实时获取各个服务的所有接口 + management.context-path: ${server.servlet.context-path:}${spring.mvc.servlet.path:}${management.endpoints.web.base-path:} + grayversion: zuihou + +# 只能配置在bootstrap.yml ,否则会生成 log.path_IS_UNDEFINED 文件夹 +# window会自动在 代码所在盘 根目录下自动创建文件夹, 如: D:/data/projects/logs +logging: + file: + # 为什么要用绝对路径,而非相对路径? 正式环境服务器磁盘通常是外挂到某个目录的,所以需要指定这个路径。 开发环境将所有系统日志存放在一起,便于后期清理日志文件,不用一个项目一个项目删除。 + path: @logging.file.path@ + name: ${logging.file.path}/${spring.application.name}/root.log + level: + com.baomidou.dynamic: debug + +# 用于/actuator/info +info: + name: '@project.name@' + description: '@project.description@' + version: '@project.version@' diff --git a/blade-service/blade-file/src/test/java/org/springblade/AppTest.java b/blade-service/blade-file/src/test/java/org/springblade/AppTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1359a54c38e922f7dddaf46462eb46cfcff2f131 --- /dev/null +++ b/blade-service/blade-file/src/test/java/org/springblade/AppTest.java @@ -0,0 +1,20 @@ +package org.springblade; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/blade-service/blade-stock/README.md b/blade-service/blade-stock/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4aaa273cb76266459d80efc700a1e9db8674903b --- /dev/null +++ b/blade-service/blade-stock/README.md @@ -0,0 +1,50 @@ +## balde-stock 模块 + 股票分析、统计模块 + 1. 行情策略筛选出自选 + 2. 自选历史统计 + 3. 操作模拟统计 + +## 架构图 + +## 工程结构 +``` +SpringBlade +├── blade-auth -- 授权服务提供 +├── blade-common -- 常用工具封装包 +├── blade-gateway -- Spring Cloud 网关 +├── blade-ops -- 运维中心 +├ ├── blade-admin -- spring-cloud后台管理 +├ ├── blade-develop -- 代码生成 +├ ├── blade-resource -- 资源管理 +├ ├── blade-seata-order -- seata分布式事务demo +├ ├── blade-seata-storage -- seata分布式事务demo +├── blade-service -- 业务模块 +├ ├── blade-desk -- 工作台模块 +├ ├── blade-log -- 日志模块 +├ ├── blade-system -- 系统模块 +├ └── blade-user -- 用户模块 +├── blade-service-api -- 业务模块api封装 +├ ├── blade-desk-api -- 工作台api +├ ├── blade-dict-api -- 字典api +├ ├── blade-system-api -- 系统api +└── └── blade-user-api -- 用户api +``` + +## 官网 + + +## 在线演示 +* Saber-基于Vue:[https://saber.bladex.vip](https://saber.bladex.vip) +* Sword-基于React:[https://sword.bladex.vip](https://sword.bladex.vip) +* Archer-全能代码生成系统:[https://archer.bladex.vip](https://archer.bladex.vip) +* Caster-数据大屏展示系统:[https://data.avuejs.com](https://data.avuejs.com) + +## 技术文档 +* [SpringBlade开发手册一览](https://gitee.com/smallc/SpringBlade/wikis/SpringBlade开发手册) +* [常见问题集锦](https://sns.bladex.vip/article-14966.html) + +## 项目地址 + +# 开源协议 +Apache Licence 2.0 ([英文原文](http://www.apache.org/licenses/LICENSE-2.0.html)) +Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件)。 diff --git a/blade-service/blade-stock/pom.xml b/blade-service/blade-stock/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..0ccf5f227ebc521293ddca54984baa8dd5b75ad6 --- /dev/null +++ b/blade-service/blade-stock/pom.xml @@ -0,0 +1,63 @@ + + + + + blade-service + org.springblade + 2.7.1 + + 4.0.0 + blade-stock + ${project.artifactId} + ${blade.project.version} + jar + + + + org.springblade + blade-core-boot + ${blade.tool.version} + + + org.springblade + blade-stock-api + ${blade.project.version} + + + + + + + + + org.springframework.boot + spring-boot-starter-test + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + package + + run + + + + + + + + + + + + + diff --git a/blade-service/blade-stock/src/main/java/org/springblade/stock/StockApplication.java b/blade-service/blade-stock/src/main/java/org/springblade/stock/StockApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..f4b314e99d1cae0519c4d916484dd7495fe6f486 --- /dev/null +++ b/blade-service/blade-stock/src/main/java/org/springblade/stock/StockApplication.java @@ -0,0 +1,19 @@ +package org.springblade.stock; + +import org.springblade.core.launch.BladeApplication; +import org.springblade.core.launch.constant.AppConstant; +import org.springframework.cloud.client.SpringCloudApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * stock启动器 + */ +@SpringCloudApplication +@EnableFeignClients(AppConstant.BASE_PACKAGES) +public class StockApplication { + + public static void main(String[] args) { + //AppConstant.APPLICATION_DESK_NAME + BladeApplication.run("blade-stock", StockApplication.class, args); + } +} diff --git a/blade-service/blade-stock/src/main/resources/application-dev.yml b/blade-service/blade-stock/src/main/resources/application-dev.yml new file mode 100644 index 0000000000000000000000000000000000000000..429dac954d62fe9acda0768a1853caff603d6456 --- /dev/null +++ b/blade-service/blade-stock/src/main/resources/application-dev.yml @@ -0,0 +1,10 @@ +#服务器端口 +server: + port: 8201 + +#数据源配置 +spring: + datasource: + url: ${blade.datasource.dev.url} + username: ${blade.datasource.dev.username} + password: ${blade.datasource.dev.password} diff --git a/blade-service/blade-stock/src/main/resources/application-prod.yml b/blade-service/blade-stock/src/main/resources/application-prod.yml new file mode 100644 index 0000000000000000000000000000000000000000..361c857e774d91dc1c0a23980c27f907d65afc0f --- /dev/null +++ b/blade-service/blade-stock/src/main/resources/application-prod.yml @@ -0,0 +1,10 @@ +#服务器端口 +server: + port: 8201 + +#数据源配置 +spring: + datasource: + url: ${blade.datasource.prod.url} + username: ${blade.datasource.prod.username} + password: ${blade.datasource.prod.password} diff --git a/blade-service/blade-stock/src/main/resources/application-test.yml b/blade-service/blade-stock/src/main/resources/application-test.yml new file mode 100644 index 0000000000000000000000000000000000000000..9e9b257d33a4d1abacdac15f9b6be24b0b4296ad --- /dev/null +++ b/blade-service/blade-stock/src/main/resources/application-test.yml @@ -0,0 +1,10 @@ +#服务器端口 +server: + port: 8201 + +#数据源配置 +spring: + datasource: + url: ${blade.datasource.test.url} + username: ${blade.datasource.test.username} + password: ${blade.datasource.test.password} diff --git a/blade-service/blade-system/src/main/resources/application-dev.yml b/blade-service/blade-system/src/main/resources/application-dev.yml index 216bd19835ff24d91c833bf0e5263b6f9a109ff7..59c9bb9a7c874e912e582451840e22c2823ff592 100644 --- a/blade-service/blade-system/src/main/resources/application-dev.yml +++ b/blade-service/blade-system/src/main/resources/application-dev.yml @@ -5,6 +5,7 @@ server: #数据源配置 spring: datasource: + driver-class-name: com.mysql.cj.jdbc.Driver url: ${blade.datasource.dev.url} username: ${blade.datasource.dev.username} - password: ${blade.datasource.dev.password} \ No newline at end of file + password: ${blade.datasource.dev.password} diff --git a/blade-service/pom.xml b/blade-service/pom.xml index 609d4a39ec862dc43a6d748f91ca16dc41d191a5..1fe200980f4810a618f80be254650ce01d3878a8 100644 --- a/blade-service/pom.xml +++ b/blade-service/pom.xml @@ -22,6 +22,8 @@ blade-system blade-user blade-demo + blade-stock + blade-file diff --git "a/doc/digital/\344\273\243\347\240\201\347\224\237\346\210\220\346\226\207\344\273\266/sql/walletrecord.menu.mysql" "b/doc/digital/\344\273\243\347\240\201\347\224\237\346\210\220\346\226\207\344\273\266/sql/walletrecord.menu.mysql" new file mode 100644 index 0000000000000000000000000000000000000000..4e778e5549dc34332f75702cd278cc22359c560b --- /dev/null +++ "b/doc/digital/\344\273\243\347\240\201\347\224\237\346\210\220\346\226\207\344\273\266/sql/walletrecord.menu.mysql" @@ -0,0 +1,11 @@ +INSERT INTO `blade_menu`(`id`,`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`) +VALUES (1293811285207400451,1293811285207400450, 'walletrecord', '钱包信息记录管理', 'menu', '/wallet/walletrecord', NULL, 1, 1, 0, 1, NULL, 0); +set @parentid = (SELECT LAST_INSERT_ID()); +INSERT INTO `blade_menu`(`id`,`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`) +VALUES (1293811285207400452,@parentid, 'walletrecord_add', '新增', 'add', '/wallet/walletrecord/add', 'plus', 1, 2, 1, 1, NULL, 0); +INSERT INTO `blade_menu`(`id`,`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`) +VALUES (1293811285207400453,@parentid, 'walletrecord_edit', '修改', 'edit', '/wallet/walletrecord/edit', 'form', 2, 2, 1, 2, NULL, 0); +INSERT INTO `blade_menu`(`id`,`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`) +VALUES (1293811285207400454,@parentid, 'walletrecord_delete', '删除', 'delete', '/api/digital-wallet/walletrecord/remove', 'delete', 3, 2, 1, 3, NULL, 0); +INSERT INTO `blade_menu`(`id`,`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`) +VALUES (1293811285207400455,@parentid, 'walletrecord_view', '查看', 'view', '/wallet/walletrecord/view', 'file-text', 4, 2, 1, 2, NULL, 0); diff --git a/doc/docker/auth/Dockerfile b/doc/docker/auth/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6b38f0dc82694d60019b525d6ad78afe18b0f094 --- /dev/null +++ b/doc/docker/auth/Dockerfile @@ -0,0 +1,15 @@ +FROM anapsix/alpine-java:8_server-jre_unlimited + +MAINTAINER w1999wtw3537@sina.com + +RUN mkdir -p /digital/auth + +WORKDIR /digital/auth + +EXPOSE 8010 + +ADD blade-auth.jar app.jar + +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] + +CMD ["--spring.profiles.active=dev"] diff --git a/doc/docker/auth/build b/doc/docker/auth/build new file mode 100644 index 0000000000000000000000000000000000000000..918c0a4c1e4b60e0251986e90d1407351c9785f8 --- /dev/null +++ b/doc/docker/auth/build @@ -0,0 +1,2 @@ +手动构建镜像命令:docker build -t digital-auth:2.7.1 . +手动启动命令:docker run -d -p 8010:8010 --name digital-auth digital-auth:2.7.1 diff --git a/doc/docker/gateway/Dockerfile b/doc/docker/gateway/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..2808345c836662c148502d41076cc0fc5bbdd366 --- /dev/null +++ b/doc/docker/gateway/Dockerfile @@ -0,0 +1,15 @@ +FROM anapsix/alpine-java:8_server-jre_unlimited + +MAINTAINER w1999wtw3537@sina.com + +RUN mkdir -p /digital/gateway + +WORKDIR /digital/gateway + +EXPOSE 80 + +ADD blade-gateway.jar app.jar + +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] + +CMD ["--spring.profiles.active=dev"] diff --git a/doc/docker/gateway/build b/doc/docker/gateway/build new file mode 100644 index 0000000000000000000000000000000000000000..300b394317f76bd9890407886f560585423eecdd --- /dev/null +++ b/doc/docker/gateway/build @@ -0,0 +1,2 @@ +手动构建镜像命令:docker build -t digital-gateway:2.7.1 . +手动启动命令:docker run -d -p 80:80 --name digital-gateway digital-gateway:2.7.1 diff --git a/doc/docker/system/Dockerfile b/doc/docker/system/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d3050247993e5ce1c993b5af3943158acb036730 --- /dev/null +++ b/doc/docker/system/Dockerfile @@ -0,0 +1,15 @@ +FROM anapsix/alpine-java:8_server-jre_unlimited + +MAINTAINER w1999wtw3537@sina.com + +RUN mkdir -p /digital/system + +WORKDIR /digital/system + +EXPOSE 8106 + +ADD blade-system.jar app.jar + +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] + +CMD ["--spring.profiles.active=dev"] diff --git a/doc/docker/system/build b/doc/docker/system/build new file mode 100644 index 0000000000000000000000000000000000000000..00885ef193e567c7f6106beeff0f9981c9c6af7a --- /dev/null +++ b/doc/docker/system/build @@ -0,0 +1,2 @@ +手动构建镜像命令:docker build -t digital-system:2.7.1 . +手动启动命令:docker run -d -p 8106:8106 --name digital-system digital-system:2.7.1 diff --git a/doc/docker/user/Dockerfile b/doc/docker/user/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..7ac949e29cb39c9a779a048f16046c86618533f0 --- /dev/null +++ b/doc/docker/user/Dockerfile @@ -0,0 +1,15 @@ +FROM anapsix/alpine-java:8_server-jre_unlimited + +MAINTAINER w1999wtw3537@sina.com + +RUN mkdir -p /digital/user + +WORKDIR /digital/user + +EXPOSE 8102 + +ADD blade-user.jar /app.jar + +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] + +CMD ["--spring.profiles.active=dev"] diff --git a/doc/docker/user/build b/doc/docker/user/build new file mode 100644 index 0000000000000000000000000000000000000000..eb49adbcdc58c4560880ffb84dc2974e3b956e5c --- /dev/null +++ b/doc/docker/user/build @@ -0,0 +1,2 @@ +手动构建镜像命令:docker build -t digital-user:2.7.1 . +手动启动命令:docker run -d -p 8102:8102 --name digital-user digital-user:2.7.1 diff --git a/doc/docker/wallet/Dockerfile b/doc/docker/wallet/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..346d709f3c96fb2e53cb0517a5935d977898a7e9 --- /dev/null +++ b/doc/docker/wallet/Dockerfile @@ -0,0 +1,15 @@ +FROM anapsix/alpine-java:8_server-jre_unlimited + +MAINTAINER w1999wtw3537@sina.com + +RUN mkdir -p /digital/wallet + +WORKDIR /digital/wallet + +EXPOSE 8201 + +ADD digital-wallet.jar app.jar + +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] + +CMD ["--spring.profiles.active=dev"] diff --git a/doc/docker/wallet/build b/doc/docker/wallet/build new file mode 100644 index 0000000000000000000000000000000000000000..390b9629b865bf546abad4e73bb3b1fce7d5aeb3 --- /dev/null +++ b/doc/docker/wallet/build @@ -0,0 +1,2 @@ +手动构建镜像命令:docker build -t digital-wallet:2.7.1 . +手动启动命令:docker run -d -p 8201:8201 --name digital-wallet digital-wallet:2.7.1 diff --git a/doc/service/fileserver/README.md b/doc/service/fileserver/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0c09a8eddeac5abb5ebb9e849fba148f0d629117 --- /dev/null +++ b/doc/service/fileserver/README.md @@ -0,0 +1,29 @@ +# 文件服务系统设计 +###1. 需求说明 + 可伸缩、通用的文件服务器.通常后端项目可能会有头像、图片、音频、视频等上传/下载需求,这些需求都可以抽象为文件服务。 + +###2. 功能特点 + - 支持Linux(推荐)、Windows + + - 可伸缩式架构,支持部署1-N台文件服务器 + + - RESTful架构的API接口,支持多语言客户端 + + - 支持文件秒传、断点续传、远程拉取上传 + + - 支持为用户指定磁盘空间配额 + + - 支持自定义文件处理器 + +###3. 系统架构 + +文件的上传/下载通常由客户端直接与文件服务器交互,上传时需要提供代表用户身份token(由业务服务器生成),成功后会返回文件根地址。 + +也可以直接由业务服务器上传返回文件根地址给客户端 + + +###4. 技术选型以及第三方存储服务 + + + +###5. 在工程根目录的docker-compose.yml下加入配置,内容可参考如下 diff --git a/pom.xml b/pom.xml index b98d303ff42a5b157b2ea3262a34804e4ab537f2..4f3eeb5924ebeb254b23c154fcf56d7c2605163d 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,8 @@ blade-service blade-service-api blade-common + blade-digital-service + blade-digital-api