商品服務
一、品牌管理
1、效果優化與快速顯示開關
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OpNSk0Kh-1632494568601)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924210757124.png)]](https://img.uj5u.com/2021/09/26/267872261325357.png)
將逆向工程product得到的resources\src\views\modules\product檔案拷貝到achangmall/renren-fast-vue/src/views/modules/product目錄下,也就是下面的兩個檔案
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-r417UaT3-1632494568605)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924210824690.png)]](https://img.uj5u.com/2021/09/26/267872261325358.png)
brand.vue : 顯示的表單
brand-add-or-update.vue:添加和更改功能
- 但是顯示的頁面沒有新增和洗掉功能,這是因為權限控制的原因
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ExKvtYEF-1632494568618)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924210849186.png)]](https://img.uj5u.com/2021/09/26/267872261325351.png)
<el-button v-if="isAuth('product:brand:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
<el-button v-if="isAuth('product:brand:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量洗掉</el-button>
- 查看“isAuth”的定義位置:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6RLj7bxm-1632494568621)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924210930813.png)]](https://img.uj5u.com/2021/09/26/267872261325359.png)
它是在“index.js”中定義,暫時將它設定為回傳值為true,即可顯示添加和洗掉功能, 再次重繪頁面能夠看到,按鈕已經出現了:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Txj7LtDV-1632494568624)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924210951855.png)]](https://img.uj5u.com/2021/09/26/267872261325352.png)
進行添加 測驗成功
- 進行修改 也會自動回顯 build/webpack.base.conf.js 中注釋掉createLintingRule()函式體,
不進行lint語法檢
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oi1G9lOG-1632494568625)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924211413453.png)]](https://img.uj5u.com/2021/09/26/2678722613253510.png)
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6FQrTV5n-1632494568627)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924211512874.png)]](https://img.uj5u.com/2021/09/26/267872261325353.png)
- brand.vue
<template>
<div class="mod-config">
<el-form
:inline="true"
:model="dataForm"
@keyup.enter.native="getDataList()"
>
<el-form-item>
<el-input
v-model="dataForm.key"
placeholder="引數名"
clearable
></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()">查詢</el-button>
<el-button
v-if="isAuth('product:brand:save')"
type="primary"
@click="addOrUpdateHandle()"
>新增</el-button
>
<el-button
v-if="isAuth('product:brand:delete')"
type="danger"
@click="deleteHandle()"
:disabled="dataListSelections.length <= 0"
>批量洗掉</el-button
>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%"
>
<el-table-column
type="selection"
header-align="center"
align="center"
width="50"
>
</el-table-column>
<el-table-column
prop="brandId"
header-align="center"
align="center"
label="品牌id"
>
</el-table-column>
<el-table-column
prop="name"
header-align="center"
align="center"
label="品牌名"
>
</el-table-column>
<el-table-column
prop="logo"
header-align="center"
align="center"
label="品牌logo地址"
>
</el-table-column>
<el-table-column
prop="descript"
header-align="center"
align="center"
label="介紹"
>
</el-table-column>
<el-table-column
prop="showStatus"
header-align="center"
align="center"
label="顯示狀態"
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.showStatus"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
@change="updateBrandStatus(scope.row)"
>
</el-switch>
</template>
</el-table-column>
<el-table-column
prop="firstLetter"
header-align="center"
align="center"
label="檢索首字母"
>
</el-table-column>
<el-table-column
prop="sort"
header-align="center"
align="center"
label="排序"
>
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作"
>
<template slot-scope="scope">
<el-button
type="text"
size="small"
@click="addOrUpdateHandle(scope.row.brandId)"
>修改</el-button
>
<el-button
type="text"
size="small"
@click="deleteHandle(scope.row.brandId)"
>洗掉</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalPage"
layout="total, sizes, prev, pager, next, jumper"
>
</el-pagination>
<!-- 彈窗, 新增 / 修改 -->
<add-or-update
v-if="addOrUpdateVisible"
ref="addOrUpdate"
@refreshDataList="getDataList"
></add-or-update>
</div>
</template>
<script>
import AddOrUpdate from "./brand-add-or-update";
export default {
data() {
return {
dataForm: {
key: "",
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalPage: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false,
};
},
components: {
AddOrUpdate,
},
activated() {
this.getDataList();
},
methods: {
updateBrandStatus(data) {
console.log("最新資訊", data);
let { brandId, showStatus } = data;
this.$http({
url: this.$http.adornUrl("/product/brand/update"),
method: "post",
data: this.$http.adornData({ brandId, showStatus: showStatus }, false),
}).then(({ data }) => {
this.$message({
type: "success",
message: "狀態更新成功",
});
});
},
// 獲取資料串列
getDataList() {
this.dataListLoading = true;
this.$http({
url: this.$http.adornUrl("/product/brand/list"),
method: "get",
params: this.$http.adornParams({
page: this.pageIndex,
limit: this.pageSize,
key: this.dataForm.key,
}),
}).then(({ data }) => {
if (data && data.code === 0) {
this.dataList = data.page.list;
this.totalPage = data.page.totalCount;
} else {
this.dataList = [];
this.totalPage = 0;
}
this.dataListLoading = false;
});
},
// 每頁數
sizeChangeHandle(val) {
this.pageSize = val;
this.pageIndex = 1;
this.getDataList();
},
// 當前頁
currentChangeHandle(val) {
this.pageIndex = val;
this.getDataList();
},
// 多選
selectionChangeHandle(val) {
this.dataListSelections = val;
},
// 新增 / 修改
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true;
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id);
});
},
// 洗掉
deleteHandle(id) {
var ids = id
? [id]
: this.dataListSelections.map((item) => {
return item.brandId;
});
this.$confirm(
`確定對[id=${ids.join(",")}]進行[${id ? "洗掉" : "批量洗掉"}]操作?`,
"提示",
{
confirmButtonText: "確定",
cancelButtonText: "取消",
type: "warning",
}
).then(() => {
this.$http({
url: this.$http.adornUrl("/product/brand/delete"),
method: "post",
data: this.$http.adornData(ids, false),
}).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
message: "操作成功",
type: "success",
duration: 1500,
onClose: () => {
this.getDataList();
},
});
} else {
this.$message.error(data.msg);
}
});
});
},
},
};
</script>
- brand-add-or-update.vue
<template>
<el-dialog
:title="!dataForm.brandId ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible"
>
<el-form
:model="dataForm"
:rules="dataRule"
ref="dataForm"
@keyup.enter.native="dataFormSubmit()"
label-width="80px"
>
<el-form-item label="品牌名" prop="name">
<el-input v-model="dataForm.name" placeholder="品牌名"></el-input>
</el-form-item>
<el-form-item label="品牌logo地址" prop="logo">
<el-input v-model="dataForm.logo" placeholder="品牌logo地址"></el-input>
</el-form-item>
<el-form-item label="介紹" prop="descript">
<el-input v-model="dataForm.descript" placeholder="介紹"></el-input>
</el-form-item>
<el-form-item label="顯示狀態" prop="showStatus">
<el-switch
v-model="dataForm.showStatus"
active-color="#13ce66"
inactive-color="#ff4949"
>
</el-switch>
</el-form-item>
<el-form-item label="檢索首字母" prop="firstLetter">
<el-input
v-model="dataForm.firstLetter"
placeholder="檢索首字母"
></el-input>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="dataForm.sort" placeholder="排序"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">確定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data() {
return {
visible: false,
dataForm: {
brandId: 0,
name: "",
logo: "",
descript: "",
showStatus: "",
firstLetter: "",
sort: "",
},
dataRule: {
name: [{ required: true, message: "品牌名不能為空", trigger: "blur" }],
logo: [
{ required: true, message: "品牌logo地址不能為空", trigger: "blur" },
],
descript: [
{ required: true, message: "介紹不能為空", trigger: "blur" },
],
showStatus: [
{
required: true,
message: "顯示狀態[0-不顯示;1-顯示]不能為空",
trigger: "blur",
},
],
firstLetter: [
{ required: true, message: "檢索首字母不能為空", trigger: "blur" },
],
sort: [{ required: true, message: "排序不能為空", trigger: "blur" }],
},
};
},
methods: {
init(id) {
this.dataForm.brandId = id || 0;
this.visible = true;
this.$nextTick(() => {
this.$refs["dataForm"].resetFields();
if (this.dataForm.brandId) {
this.$http({
url: this.$http.adornUrl(
`/product/brand/info/${this.dataForm.brandId}`
),
method: "get",
params: this.$http.adornParams(),
}).then(({ data }) => {
if (data && data.code === 0) {
this.dataForm.name = data.brand.name;
this.dataForm.logo = data.brand.logo;
this.dataForm.descript = data.brand.descript;
this.dataForm.showStatus = data.brand.showStatus;
this.dataForm.firstLetter = data.brand.firstLetter;
this.dataForm.sort = data.brand.sort;
}
});
}
});
},
// 表單提交
dataFormSubmit() {
this.$refs["dataForm"].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(
`/product/brand/${!this.dataForm.brandId ? "save" : "update"}`
),
method: "post",
data: this.$http.adornData({
brandId: this.dataForm.brandId || undefined,
name: this.dataForm.name,
logo: this.dataForm.logo,
descript: this.dataForm.descript,
showStatus: this.dataForm.showStatus,
firstLetter: this.dataForm.firstLetter,
sort: this.dataForm.sort,
}),
}).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
message: "操作成功",
type: "success",
duration: 1500,
onClose: () => {
this.visible = false;
this.$emit("refreshDataList");
},
});
} else {
this.$message.error(data.msg);
}
});
}
});
},
},
};
</script>
2、添加上傳
這里我們選擇將圖片放置到阿里云上,使用物件存盤, 阿里云上使使用物件存盤方式:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-tBhSavFA-1632494568629)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924214001988.png)]](https://img.uj5u.com/2021/09/26/2678722613253511.png)
- 創建Bucket(作為專案)
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QkXPi5vD-1632494568631)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924214202972.png)]](https://img.uj5u.com/2021/09/26/2678722613253512.png)
- 上傳檔案:上傳成功后,取得圖片的URL
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-kXcqdpbX-1632494568633)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924214424853.png)]](https://img.uj5u.com/2021/09/26/2678722613253513.png)
- 這種方式是
手動上傳圖片,實際上我們可以在程式中設定自動上傳圖片到阿里云物件存盤,
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-AW1Pm2YC-1632494568634)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924214451666.png)]](https://img.uj5u.com/2021/09/26/2678722613253514.png)
上傳的賬號資訊存盤在應用服務器 上傳先找應用服務器要一個policy上傳策略,生成防偽簽名
-
使用代碼上傳 查看阿里云關于檔案上傳的幫助:
https://help.aliyun.com/document_detail/32009.html?spm=a2c4g.11186623.6.768.549d59aaWuZMGJ
- 在
achangmall-product/pom.xml中添加依賴包
<dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.10.2</version> </dependency>- 上傳檔案流
使用檔案上傳,您可以將本地檔案上傳到OSS檔案,
以下代碼用于將本地檔案examplefile.txt上傳到目標存盤空間examplebucket中exampledir目錄下的exampleobject.txt檔案,
// yourEndpoint填寫Bucket所在地域對應的Endpoint,以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com, String endpoint = "yourEndpoint"; // 阿里云賬號AccessKey擁有所有API的訪問權限,風險很高,強烈建議您創建并使用RAM用戶進行API訪問或日常運維,請登錄RAM控制臺創建RAM用戶, String accessKeyId = "yourAccessKeyId"; String accessKeySecret = "yourAccessKeySecret"; // 創建OSSClient實體, OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); // 創建PutObjectRequest物件, // 依次填寫Bucket名稱(例如examplebucket)、Object完整路徑(例如exampledir/exampleobject.txt)和本地檔案的完整路徑,Object完整路徑中不能包含Bucket名稱, // 如果未指定本地路徑,則默認從示例程式所屬專案對應本地路徑中上傳檔案, PutObjectRequest putObjectRequest = new PutObjectRequest("examplebucket", "exampledir/exampleobject.txt", new File("D:\\localpath\\examplefile.txt")); // 如果需要上傳時設定存盤型別和訪問權限,請參考以下示例代碼, // ObjectMetadata metadata = new ObjectMetadata(); // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString()); // metadata.setObjectAcl(CannedAccessControlList.Private); // putObjectRequest.setMetadata(metadata); // 上傳檔案, ossClient.putObject(putObjectRequest); // 關閉OSSClient, ossClient.shutdown();上面代碼的資訊可以通過如下查找:
-
endpoint的取值:
-
點擊概覽就可以看到你的endpoint資訊,endpoint在這里就是上海等地區,如 oss-cn-qingdao.aliyuncs.com
-
bucket域名:
-
就是簽名加上bucket,如achangmall0.oss-cn-hangzhou.aliyuncs.com
accessKeyId和accessKeySecret需要創建一個RAM賬號:![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gGxMIFAU-1632494568636)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924214912162.png)]](https://img.uj5u.com/2021/09/26/2678722613253515.png)
-
選上編程訪問 創建用戶完畢后,會得到一個“AccessKey ID”和“AccessKeySecret”,
然后復制這兩個值到代碼的“AccessKey ID”和“AccessKeySecret”,
另外還需要添加訪問控制權限:
@Test void test0() throws FileNotFoundException { // Endpoint以杭州為例,其它Region請按實際情況填寫, String endpoint = "oss-cn-hangzhou.aliyuncs.com"; // 云賬號AccessKey有所有API訪問權限,建議遵循阿里云安全最佳實踐,創建并使用RAM子賬號進行API訪問或日常運維,請登錄 https://ram.console.aliyun.com 創建, String accessKeyId = "你的accessKeyId"; String accessKeySecret = "你的accessKeySecret"; // 創建OSSClient實體, OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); // 上傳檔案流, InputStream inputStream = new FileInputStream("C:\\Users\\PePe\\Pictures\\Camera Roll\\321.png"); ossClient.putObject("achangmall0", "321.png", inputStream); // 關閉OSSClient, ossClient.shutdown(); System.out.println("上傳成功."); } -
更為簡單的使用方式,是使用
SpringCloud Alibabahttps://github.com/alibaba/aliyun-spring-boot/blob/master/aliyun-spring-boot-samples/aliyun-oss-spring-boot-sample/README-zh.md
- 在
achangmall-common/pom.xml引入依賴
具體的可以在maven中央倉庫查找
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alicloud-oss</artifactId> <version>2.1.1.RELEASE</version> </dependency>- 在組態檔中配置 OSS 服務對應的 accessKey、secretKey 和 endpoint,
alicloud: access-key: xxx secret-key: xxx oss: endpoint: oss-cn-hangzhou.aliyuncs.com- 注入OSSClient并進行檔案上傳下載等操作
@RunWith(SpringRunner.class) @SpringBootTest public class OssTest { @Resource private OSSClient ossClient; @Test void test1() throws FileNotFoundException { // 上傳檔案流, InputStream inputStream = new FileInputStream("C:\\Users\\PePe\\Pictures\\Camera Roll\\321.png"); ossClient.putObject("achangmall0", "321.png", inputStream); // 關閉OSSClient, ossClient.shutdown(); System.out.println("上傳完成..."); } } - 在
- 在
但是這樣來做還是比較麻煩,如果以后的上傳任務都交給achangmall-product來完成,顯然耦合度高,最好單獨新建一個Module來完成檔案上傳任務,
- 創建第三方模塊
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1AAAstll-1632494568639)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924223000398.png)]](https://img.uj5u.com/2021/09/26/2678722613253516.png)
- 添加依賴,將原來achangmall-common中的“spring-cloud-starter-alicloud-oss”依賴移動到該專案中
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alicloud-oss</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.achang.achangmall</groupId>
<artifactId>achangmall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
-
主啟動類@EnableDiscoveryClient // 在主啟動類中開啟服務的注冊和發現
-
在nacos中注冊 在nacos創建命名空間“ achangmall-third-party ”
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Q28ssfNy-1632494568640)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924223417154.png)]](https://img.uj5u.com/2021/09/26/2678722613253517.png)
- 在“ achangmall-third-party”命名空間中,創建“ achangmall-third-service.yml”檔案
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-78xlpcNq-1632494568642)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210924223642717.png)]](https://img.uj5u.com/2021/09/26/2678722613253518.png)
- 撰寫組態檔 application.yml
server:
port: 30000
spring:
application:
name: achangmall-third-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
- bootstrap.properties
spring.cloud.nacos.config.name=achangmall-third-service
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=ad0431b9-a77f-4220-bf61-b48c7e117250
spring.cloud.nacos.config.extension-configs[0].data-id=achangmall-third-service.yml
spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true
- 撰寫測驗類
@SpringBootTest
@RunWith(SpringRunner.class)
class AchangmallThirdServiceApplicationTests {
@Resource
OSSClient ossClient;
@Test
void contextLoads() throws FileNotFoundException {
//上傳檔案流,
InputStream inputStream = new FileInputStream("C:\\Users\\PePe\\Pictures\\Camera Roll\\123.jpg");
ossClient.putObject("achangmall0", "333.jpg", inputStream);
// 關閉OSSClient,
ossClient.shutdown();
System.out.println("上傳成功.");
}
}
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xprgkA72-1632569865581)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925142123288.png)]](https://img.uj5u.com/2021/09/26/2678722613253519.png)
- 改進:服務端簽名后直傳
采用JavaScript客戶端直接簽名(參見JavaScript客戶端簽名直傳)時,AccessKeyID和AcessKeySecret會暴露在前端頁面,因此存在嚴重的安全隱患,
因此,OSS提供了服務端簽名后直傳的方案,
- 向服務器獲取到簽名,再去請求oss服務器
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ALS8Vv8B-1632569865620)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925142916018.png)]](https://img.uj5u.com/2021/09/26/2678722613253520.png)
- 服務端簽名后直傳的原理如下:
用戶發送上傳Policy請求到應用服務器, 應用服務器回傳上傳Policy和簽名給用戶,
用戶直接上傳資料到OSS,
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-7ppRPzbz-1632569865623)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925142939283.png)]](https://img.uj5u.com/2021/09/26/2678722613253521.png)
- 在com.achang.achangmall.controller.OssController撰寫controller
/******
@author 阿昌
@create 2021-09-25 14:32
*******
*/
@RestController
@RequestMapping("third-service/oss")
public class OssController {
@Resource
private OSSClient ossClient;
@Value("${spring.cloud.alicloud.oss.endpoint}")
public String endpoint;
@Value("${spring.cloud.alicloud.oss.bucket}")
public String bucket;
@Value("${spring.cloud.alicloud.access-key}")
public String accessId;
private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
@GetMapping("/policy")
public Map<String, String> getPolicy(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String host = "https://" + bucket + "." + endpoint; // host的格式為 bucketname.endpoint
// callbackUrl為上傳回呼服務器的URL,請將下面的IP和Port配置為您自己的真實資訊,
// String callbackUrl = "http://88.88.88.88:8888";
String dir = format.format(new Date())+"/"; // 用戶上傳檔案時指定的前綴,以日期格式存盤
// 創建OSSClient實體,
Map<String, String> respMap= null;
try {
long expireTime = 30;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
// PostObject請求最大可支持的檔案大小為5 GB,即CONTENT_LENGTH_RANGE為5*1024*1024*1024,
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);//生成協議秘鑰
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
respMap = new LinkedHashMap<String, String>();
respMap.put("accessid", accessId);
respMap.put("policy", encodedPolicy);//生成的協議秘鑰
respMap.put("signature", postSignature);
respMap.put("dir", dir);
respMap.put("host", host);
respMap.put("expire", String.valueOf(expireEndTime / 1000));
// respMap.put("expire", formatISO8601Date(expiration));
} catch (Exception e) {
// Assert.fail(e.getMessage());
System.out.println(e.getMessage());
} finally {
ossClient.shutdown();
}
return respMap;
}
}
-
測驗請求,http://localhost:30000/third-service/oss/policy,成功獲取
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LLWMloyp-1632569865627)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925145932172.png)]](https://img.uj5u.com/2021/09/26/2678722613253522.png)
-
然后,我們通過gateway網關代理轉發,在上傳檔案時的訪問路徑為“ http://localhost:88/api/third/oss/policy”,
-
配置網關
- id: oss_route
uri: lb://achangmall-third-service
predicates:
- Path=/api/third-service/**
filters:
- RewritePath=/api/third-service/(?<segment>.*),/$\{segment}
- 訪問http://localhost:88/api/third-service/third-service/oss/policy,測驗是否可以轉發到我們的介面,如下成功訪問
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-d23vQJT6-1632569865630)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925151233915.png)]](https://img.uj5u.com/2021/09/26/2678722613253523.png)
-
上傳組件
-
放置專案提供的upload檔案夾到components/目錄下,一個是單檔案上傳,另外一個是多檔案上傳
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-HH9a8Ydn-1632569865632)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925153610191.png)]](https://img.uj5u.com/2021/09/26/267872261325354.png)
-
policy.js封裝一個Promise,發送/thirdparty/oss/policy請求,
vue專案會自動加上api前綴 -
multiUpload.vue多檔案上傳,要改,改方式如下
-
singleUpload.vue單檔案上傳,
- 要替換里面的action中的內容action=“http://achangmall0.oss-cn-hangzhou.aliyuncs.com”,
你的阿里云指定的bucket域名
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fEXmw90m-1632569865633)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925153721592.png)]](https://img.uj5u.com/2021/09/26/267872261325355.png)
- 要替換里面的action中的內容action=“http://achangmall0.oss-cn-hangzhou.aliyuncs.com”,
-
singleUpload.vue代碼
<template>
<div>
<el-upload
action="http://achangmall0.oss-cn-hangzhou.aliyuncs.com"
:data="dataObj"
list-type="picture"
:multiple="false"
:show-file-list="showFileList"
:file-list="fileList"
:before-upload="beforeUpload"
:on-remove="handleRemove"
:on-success="handleUploadSuccess"
:on-preview="handlePreview"
>
<el-button size="small" type="primary">點擊上傳</el-button>
<div slot="tip" class="el-upload__tip">
只能上傳jpg/png檔案,且不超過10MB
</div>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="fileList[0].url" alt="" />
</el-dialog>
</div>
</template>
<script>
import { policy } from "./policy";
import { getUUID } from "@/utils";
export default {
name: "singleUpload",
props: {
value: String,
},
computed: {
imageUrl() {
return this.value;
},
imageName() {
if (this.value != null && this.value !== "") {
return this.value.substr(this.value.lastIndexOf("/") + 1);
} else {
return null;
}
},
fileList() {
return [
{
name: this.imageName,
url: this.imageUrl,
},
];
},
showFileList: {
get: function () {
return (
this.value !== null && this.value !== "" && this.value !== undefined
);
},
set: function (newValue) {},
},
},
data() {
return {
dataObj: {
policy: "",
signature: "",
key: "",
ossaccessKeyId: "",
dir: "",
host: "",
// callback:'',
},
dialogVisible: false,
};
},
methods: {
emitInput(val) {
this.$emit("input", val);
},
handleRemove(file, fileList) {
this.emitInput("");
},
handlePreview(file) {
this.dialogVisible = true;
},
beforeUpload(file) {
let _self = this;
return new Promise((resolve, reject) => {
policy()
.then((response) => {
console.log("回應的資料", response);
_self.dataObj.policy = response.policy;
_self.dataObj.signature = response.signature;
_self.dataObj.ossaccessKeyId = response.accessid;
_self.dataObj.key = response.dir + getUUID() + "_${filename}";
_self.dataObj.dir = response.dir;
_self.dataObj.host = response.host;
console.log("回應的資料222,,,", _self.dataObj);
resolve(true);
})
.catch((err) => {
reject(false);
});
});
},
handleUploadSuccess(res, file) {
console.log("上傳成功...");
this.showFileList = true;
this.fileList.pop();
this.fileList.push({
name: file.name,
url:
this.dataObj.host +
"/" +
this.dataObj.key.replace("${filename}", file.name),
});
this.emitInput(this.fileList[0].url);
},
},
};
</script>
<style>
</style>
- multiUpload.vue代碼
<template>
<div>
<el-upload
action="http://achangmall0.oss-cn-hangzhou.aliyuncs.com"
:data="dataObj"
:list-type="listType"
:file-list="fileList"
:before-upload="beforeUpload"
:on-remove="handleRemove"
:on-success="handleUploadSuccess"
:on-preview="handlePreview"
:limit="maxCount"
:on-exceed="handleExceed"
:show-file-list="showFile"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt />
</el-dialog>
</div>
</template>
<script>
import { policy } from "./policy";
import { getUUID } from "@/utils";
export default {
name: "multiUpload",
props: {
//圖片屬性陣列
value: Array,
//最大上傳圖片數量
maxCount: {
type: Number,
default: 30,
},
listType: {
type: String,
default: "picture-card",
},
showFile: {
type: Boolean,
default: true,
},
},
data() {
return {
dataObj: {
policy: "",
signature: "",
key: "",
ossaccessKeyId: "",
dir: "",
host: "",
uuid: "",
},
dialogVisible: false,
dialogImageUrl: null,
};
},
computed: {
fileList() {
let fileList = [];
for (let i = 0; i < this.value.length; i++) {
fileList.push({ url: this.value[i] });
}
return fileList;
},
},
mounted() {},
methods: {
emitInput(fileList) {
let value = [];
for (let i = 0; i < fileList.length; i++) {
value.push(fileList[i].url);
}
this.$emit("input", value);
},
handleRemove(file, fileList) {
this.emitInput(fileList);
},
handlePreview(file) {
this.dialogVisible = true;
this.dialogImageUrl = file.url;
},
beforeUpload(file) {
let _self = this;
return new Promise((resolve, reject) => {
policy()
.then((response) => {
console.log("這是什么${filename}");
_self.dataObj.policy = response.data.policy;
_self.dataObj.signature = response.data.signature;
_self.dataObj.ossaccessKeyId = response.data.accessid;
_self.dataObj.key = response.data.dir + getUUID() + "_${filename}";
_self.dataObj.dir = response.data.dir;
_self.dataObj.host = response.data.host;
resolve(true);
})
.catch((err) => {
console.log("出錯了...", err);
reject(false);
});
});
},
handleUploadSuccess(res, file) {
this.fileList.push({
name: file.name,
// url: this.dataObj.host + "/" + this.dataObj.dir + "/" + file.name; 替換${filename}為真正的檔案名
url:
this.dataObj.host +
"/" +
this.dataObj.key.replace("${filename}", file.name),
});
this.emitInput(this.fileList);
},
handleExceed(files, fileList) {
this.$message({
message: "最多只能上傳" + this.maxCount + "張圖片",
type: "warning",
duration: 1000,
});
},
},
};
</script>
<style>
</style>
- policy.js代碼
/third-service/third-service/oss/policy為你88網關代理的oss服務路由uri地址
import http from '@/utils/httpRequest.js'
export function policy() {
return new Promise((resolve, reject) => {
http({
#修改你88網關代理的oss服務路由uri地址
url: http.adornUrl("/third-service/third-service/oss/policy"),
method: "get",
params: http.adornParams({})
}).then(({ data }) => {
resolve(data);
})
});
}
- 我們在后端準備好了簽名controller,那么前端是在哪里獲取的呢
而檔案上傳前呼叫的方法::before-upload=“beforeUpload”發現該方法回傳了一個new Promise,呼叫了policy(),該方法是policy.js中的 import { policy } from “./policy”;
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-vUtYJzG6-1632569865634)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925154037083.png)]](https://img.uj5u.com/2021/09/26/2678722613253524.png)
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-e9uPor6J-1632569865635)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925154122803.png)]](https://img.uj5u.com/2021/09/26/2678722613253525.png)
- 在vue中看是response.data.policy,在控制臺看response.policy,所以去java里面改回傳值為
R,return R.ok().put(“data”,respMap); - 也可以像上面,阿昌這樣子
直接修改前端代碼,選擇一個即可
-
阿里云開啟跨域
開始執行上傳,但是在上傳程序中,出現了跨域請求問題:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XndpSwcj-1632569865637)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925154348021.png)]](https://img.uj5u.com/2021/09/26/267872261325356.png)
這又是一個跨域的問題,解決方法就是在阿里云上開啟跨域訪問:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Z7sHad9c-1632569865638)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925154413963.png)]](https://img.uj5u.com/2021/09/26/2678722613253526.png)
-
配置oss跨域
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-biv0fprv-1632569865639)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925154518787.png)]](https://img.uj5u.com/2021/09/26/2678722613253527.png)
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-nX1EsR4Z-1632569865640)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925154606416.png)]](https://img.uj5u.com/2021/09/26/2678722613253528.png)
再次執行檔案上傳, 注意上傳時他的key變成了response.dir +getUUID()+"_${filename}";
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DVYJSPjs-1632569865641)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925154634920.png)]](https://img.uj5u.com/2021/09/26/2678722613253529.png)
3、表單校驗&自定義校驗器
- 修改brand-add-or-update如下: :active-value=“1” :inactive-value=“0” # 激活為1,不激活為0
<el-switch
v-model="dataForm.showStatus"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
>
</el-switch>
- 添加表單校驗&自定義校驗器
<script>
firstLetter: [
{
validator: (rule, value, callback) => {
if (value == "") {
callback(new Error("首字母必須填寫"));
} else if (!/^[a-zA-Z]$/.test(value)) {
callback(new Error("首字母必須a-z或者A-Z之間"));
} else {
callback();
}
},
trigger: "blur",
},
],
sort: [{validator: (rule, value, callback) => {
if (value == "") {
callback(new Error("排序欄位必須填寫"));
} else if (!Number.isInteger(parseInt(value)) || parseInt(value) < 0){
callback(new Error("排序欄位必須是一個整數"));
} else {
callback();
}
}, trigger: "blur" }]
</script>
- 完整brand-add-or-update修改代碼
<template>
<el-dialog
:title="!dataForm.brandId ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible"
>
<el-form
:model="dataForm"
:rules="dataRule"
ref="dataForm"
@keyup.enter.native="dataFormSubmit()"
label-width="80px"
>
<el-form-item label="品牌名" prop="name">
<el-input v-model="dataForm.name" placeholder="品牌名"></el-input>
</el-form-item>
<el-form-item label="品牌logo地址" prop="logo">
<!-- <el-input v-model="dataForm.logo" placeholder="品牌logo地址"></el-input> -->
<singleUpload v-model="dataForm.logo"></singleUpload>
</el-form-item>
<el-form-item label="介紹" prop="descript">
<el-input v-model="dataForm.descript" placeholder="介紹"></el-input>
</el-form-item>
<el-form-item label="顯示狀態" prop="showStatus">
<el-switch
v-model="dataForm.showStatus"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
>
</el-switch>
</el-form-item>
<el-form-item label="檢索首字母" prop="firstLetter">
<el-input
v-model="dataForm.firstLetter"
placeholder="檢索首字母"
></el-input>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="dataForm.sort" placeholder="排序"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">確定</el-button>
</span>
</el-dialog>
</template>
<script>
import singleUpload from "@/components/upload/singleUpload";
export default {
components: {
singleUpload: singleUpload,
},
data() {
return {
visible: false,
dataForm: {
brandId: 0,
name: "",
logo: "",
descript: "",
showStatus: "",
firstLetter: "",
sort: "",
},
dataRule: {
name: [{ required: true, message: "品牌名不能為空", trigger: "blur" }],
logo: [
{ required: true, message: "品牌logo地址不能為空", trigger: "blur" },
],
descript: [
{ required: true, message: "介紹不能為空", trigger: "blur" },
],
showStatus: [
{
required: true,
message: "顯示狀態[0-不顯示;1-顯示]不能為空",
trigger: "blur",
},
],
firstLetter: [
{
validator: (rule, value, callback) => {
if (value == "") {
callback(new Error("首字母必須填寫"));
} else if (!/^[a-zA-Z]$/.test(value)) {
callback(new Error("首字母必須a-z或者A-Z之間"));
} else {
callback();
}
},
trigger: "blur",
},
],
sort: [
{
validator: (rule, value, callback) => {
if (value == "") {
callback(new Error("排序欄位必須填寫"));
} else if (
!Number.isInteger(parseInt(value)) ||
parseInt(value) < 0
) {
callback(new Error("排序欄位必須是一個整數"));
} else {
callback();
}
},
trigger: "blur",
},
],
},
};
},
methods: {
init(id) {
this.dataForm.brandId = id || 0;
this.visible = true;
this.$nextTick(() => {
this.$refs["dataForm"].resetFields();
if (this.dataForm.brandId) {
this.$http({
url: this.$http.adornUrl(
`/product/brand/info/${this.dataForm.brandId}`
),
method: "get",
params: this.$http.adornParams(),
}).then(({ data }) => {
if (data && data.code === 0) {
this.dataForm.name = data.brand.name;
this.dataForm.logo = data.brand.logo;
this.dataForm.descript = data.brand.descript;
this.dataForm.showStatus = data.brand.showStatus;
this.dataForm.firstLetter = data.brand.firstLetter;
this.dataForm.sort = data.brand.sort;
}
});
}
});
},
// 表單提交
dataFormSubmit() {
this.$refs["dataForm"].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(
`/product/brand/${!this.dataForm.brandId ? "save" : "update"}`
),
method: "post",
data: this.$http.adornData({
brandId: this.dataForm.brandId || undefined,
name: this.dataForm.name,
logo: this.dataForm.logo,
descript: this.dataForm.descript,
showStatus: this.dataForm.showStatus,
firstLetter: this.dataForm.firstLetter,
sort: this.dataForm.sort,
}),
}).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
message: "操作成功",
type: "success",
duration: 1500,
onClose: () => {
this.visible = false;
this.$emit("refreshDataList");
},
});
} else {
this.$message.error(data.msg);
}
});
}
});
},
},
};
</script>
4、JSR303資料校驗
-
問題引入:
填寫form時應該有前端校驗,后端也應該有校驗 前端 前端的校驗是element-ui表單驗證 Form 組件提供了表單驗證的功能,只需要通過 rules 屬性傳入約定的驗證規則,并將 Form-Item 的 prop 屬性設定為需校驗的欄位名即可,
-
如果你的springboot版本沒有默認引入,就匯入依賴
<!--jsr3引數校驗器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
里面依賴了hibernate-validator 在非空處理方式上提供了@NotNull,@NotBlank和@NotEmpty
在物體類的屬性上使用如上的注解等
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank
private String name;
/**
* 品牌logo地址
*/
private String logo;
/**
* 介紹
*/
private String descript;
/**
* 顯示狀態[0-不顯示;1-顯示]
*/
@NotNull
private Integer showStatus;
/**
* 檢索首字母
*/
@NotEmpty
private String firstLetter;
/**
* 排序
*/
@NotNull
@Min(0)
private Integer sort;
}
- 步驟2:controller中加校驗注解
@Valid,開啟校驗,
@RequestMapping("/save")
public R save(@RequestBody @Valid BrandEntity brand){
brandService.save(brand);
return R.ok();
}
-
可以在添加注解的時候,修改
message@NotBlank(message = "品牌名必須非空") private String name; -
但是這種回傳的錯誤結果并不符合我們的業務需要,
-
步驟3:給校驗的Bean后,緊跟一個
BindResult,就可以獲取到校驗的結果,拿到校驗的結果,就可以自定義的封裝,
@RequestMapping("/save")
public R save(@RequestBody @Valid BrandEntity brand, BindingResult result){
if( result.hasErrors()) {
Map<String, String> map = new HashMap<>();
//1.獲取錯誤的校驗結果
result.getFieldErrors().forEach((item) -> {
//獲取發生錯誤時的message
String message = item.getDefaultMessage();
//獲取發生錯誤的欄位
String field = item.getField();
map.put(field, message);
});
return R.error(400, "提交的資料不合法").put("data", map);
}
brandService.save(brand);
return R.ok();
}
這種是針對于該請求設定了一個內容校驗,如果針對于每個請求都單獨進行配置,顯然不是太合適,實際上可以統一的對于例外進行處理,
統一例外處理@ControllerAdvice步驟4:統一例外處理
可以使用SpringMvc所提供的@ControllerAdvice,通過“basePackages”能夠說明處理哪些路徑下的例外,
在com.achang.achangmall.product.exception.AchangExceptionControllerAdvice撰寫
@Slf4j
@RestControllerAdvice(basePackages = "com.achang.achangmall.product")
public class AchangExceptionControllerAdvice {
@ExceptionHandler(value = Exception.class) // 也可以回傳ModelAndView
public R handleValidException(MethodArgumentNotValidException exception) {
Map<String, String> map = new HashMap<>();
// 獲取資料校驗的錯誤結果
BindingResult bindingResult = exception.getBindingResult();
bindingResult.getFieldErrors().forEach(fieldError -> {
String message = fieldError.getDefaultMessage();
String field = fieldError.getField();
map.put(field, message);
});
log.error("資料校驗出現問題{},例外型別{}", exception.getMessage(), exception.getClass());
return R.error(400, "資料校驗出現問題").put("data", map);
}
}
- 測驗: http://localhost:88/api/product/brand/save
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fVEYvtja-1632569865643)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925174005562.png)]](https://img.uj5u.com/2021/09/26/2678722613253530.png)
如果沒有用,可能是spring沒有掃描到,在主函式上添加
@ComponentScan("com.achang.achangmall.product")
- 默認例外處理
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
log.error("未知例外{},例外型別{}",throwable.getMessage(),throwable.getClass());
return R.error(400,"資料校驗出現問題");
}
- 錯誤狀態碼
上面代碼中,針對于錯誤狀態碼,是我們進行隨意定義的,然而正規開發程序中,錯誤狀態碼有著嚴格的定義規則,如該在專案中我們的錯誤狀態碼定義
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-E3QMr2jz-1632569865643)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925173220257.png)]](https://img.uj5u.com/2021/09/26/2678722613253531.png)
- 為了定義這些錯誤狀態碼,我們可以單獨定義一個常量類,用來存盤這些錯誤狀態碼
com.achang.common.exception,在通用模塊中
/***
* 錯誤碼和錯誤資訊定義類
* 1. 錯誤碼定義規則為5為數字
* 2. 前兩位表示業務場景,最后三位表示錯誤碼,例如:100001,10:通用 001:系統未知例外
* 3. 維護錯誤碼后需要維護錯誤描述,將他們定義為列舉形式
* 錯誤碼串列:
* 10: 通用
* 001:引數格式校驗
* 11: 商品
* 12: 訂單
* 13: 購物車
* 14: 物流
*/
public enum BizCodeEnum {
UNKNOW_EXEPTION(10000,"系統未知例外"),
VALID_EXCEPTION( 10001,"引數格式校驗失敗");
private int code;
private String msg;
BizCodeEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
5、分組校驗功能(完成多場景的復雜校驗)
- 給校驗注解,標注上
groups,指定什么情況下才需要進行校驗
groups里面的內容要以介面的形式顯示出來
如:指定在更新和添加的時候,都需要進行校驗,新增時不需要帶id,修改時必須帶id
- 在通用模塊中創建校驗用的空介面,他只是個標識
achangmall-common中的com.achang.common.vail
//更新校驗
public interface UpdateVail {}
//新增校驗
public interface AddVail {}
- 在實體類上
groups標志介面標識
在這種情況下,沒有指定分組的校驗注解,默認是不起作用的,想要起作用就必須要加groups,
@NotNull(message = "修改必須定制品牌id", groups = {UpdateVailGroup.class})
@Null(message = "新增不能指定id", groups = {AddVailGroup.class})
private Long brandId;
- 業務方法引數上使用@Validated注解,并用@Validated指定使用校驗的介面標識
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-WkMeaSFd-1632569865645)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925181029419.png)]](https://img.uj5u.com/2021/09/26/2678722613253532.png)
分組情況下,校驗注解生效問題
默認情況下,在分組校驗情況下,沒有指定指定分組的校驗注解,將不會生效,它只會在不分組的情況下生效,
6、自定義校驗功能
-
場景:
- 要校驗showStatus的01狀態,可以用正則,但我們可以利用其他方式解決復雜場景,比如我們想要下面的場景
-
添加依賴
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
- 撰寫自定義的校驗注解
/**
* 自定義校驗注解
*/
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
// 使用該屬性去Validation.properties中取
String message() default "{com.atguigu.common.valid.ListValue.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] value() default {};//傳入可通過校驗的值[]
}
- 自定義校驗器
/**
* 自定義校驗器
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {//泛型左邊:自定義校驗注解,泛型右邊:校驗的型別
private Set<Integer> set=new HashSet<>();
@Override
public void initialize(ListValue constraintAnnotation) {
int[] value = constraintAnnotation.value();//獲取可通過的值
for (int i : value) {
set.add(i);
}
}
@Override//左側:傳入需要校驗的值
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
- 關聯校驗器和校驗注解
一個校驗注解可以匹配多個校驗器
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-8nENMYt0-1632569865646)(C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210925193339258.png)]](https://img.uj5u.com/2021/09/26/2678722613253533.png)
- 使用實體
/**
* 顯示狀態[0-不顯示;1-顯示]
* 標識只能接受0,1;其他值都不能通過校驗
*/
@ListValue(value = {0,1},groups ={AddGroup.class})
private Integer showStatus;
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/303061.html
標籤:其他
上一篇:vue專案實作登陸 注冊效果
下一篇:js的reduce的一些用法總結
