目录
[1. 数据库创建表SQL格式](#1. 数据库创建表SQL格式)
[2. 服务器端-获取wf门店列表与wf施工部位接口](#2. 服务器端-获取wf门店列表与wf施工部位接口)
[3. 服务器端-获取wfId接口](#3. 服务器端-获取wfId接口)
[4. 服务器端-写入WF质保,并上传图片到阿里云OSS(重点)](#4. 服务器端-写入WF质保,并上传图片到阿里云OSS(重点))
[5. 小程序端-跳转质保录入页面](#5. 小程序端-跳转质保录入页面)
[6. 小程序端-WF质保录入](#6. 小程序端-WF质保录入)
1. 数据库创建表SQL格式
我们根据现在现有的字段设置wf质保表的字段

有需求是要选择门店和施工部位的,那我就要新建两个表来存储门店列表和施工部位列表
然后先做两个接口,给前端初始化的时候获取门店列表和施工部位列表
那么SQL就有三个表:wf表,wf施工门店表,wf施工部位表
PGSQL的模式已经在上个表创建了:用户模块 和 质保模块
sql
-- WF表数据
create table 质保模块.wf表(
WFID varchar(200) primary key,
施工门店名称 varchar(50) not null,
车辆品牌 varchar(50) not null,
车型 varchar(50) not null,
车架 varchar(200) not null,
车膜型号 varchar(50) ,
施工部位 varchar(50)[],
施工时间 date,
质保年限 varchar(50),
车膜型号2 varchar(50),
施工部位2 varchar(50)[],
施工时间2 date,
质保年限2 varchar(50),
车膜型号3 varchar(50),
施工部位3 varchar(50)[],
施工时间3 date,
质保年限3 varchar(50),
参考价格 decimal(10,2),
图片上传 varchar(200),
提交人 varchar(200),
提交日期 date,
提交时间 timestamp,
审核状态 smallint default 0,
审核时间 timestamp,
审核人 varchar(20)
);
create table 质保模块.wf施工门店表(
门店ID serial primary key,
施工门店名称 varchar(50) not null
);
insert into 质保模块.wf施工门店表(施工门店名称) values
('佛山市德信汽车维修服务有限公司'),
('佛山市尊车港汽车用品有限公司'),
('广州尊车港汽车用品有限公司'),
('佛山市禅城区德信汽车服务中心'),
('广州嘉行利科技发展有限公司'),
('广州市德迪汽车贸易有限公司'),
('广州锦星行汽车贸易有限公司'),
('广州锦耀红旗体验中心'),
('肇庆锦龙汽车贸易有限公司'),
('东莞溢华汽车销售服务有限公司'),
('深圳市锦奥汽车贸易有限公司'),
('江门进口汽车服务有限公司'),
('佛山海八骏领雷克萨斯汽车销售服务有限公司'),
('深圳市锦龙汽车贸易有限公司'),
('深圳市奥德汽车贸易有限公司'),
('东莞志诚集团(PDI)'),
('广州市锦龙汽车发展有限公司'),
('广州市锦龙奥德汽车销售服务有限公司'),
('株洲宝华汽车销售服务有限公司'),
('东莞溢华之星汽车销售服务有限公司'),
('深圳市鸿友汽车销售服务有限公司'),
('东莞市鸿运汽车销售服务有限公司'),
('东莞市骏业之星汽车销售服务有限公司'),
('佛山市禅城区尊车港汽车维修有限公司');
create table 质保模块.wf施工部位表(
部位ID serial primary key,
施工部位 varchar(50) not null
);
insert into 质保模块.wf施工部位表(施工部位)
values ('全车'),('前挡窗'),('左前窗'),('右前窗'),('左后窗'),('右后窗'),('后挡窗'),('天窗');
2. 服务器端-获取wf门店列表与wf施工部位接口
先创建一个SQL语句java类接口,因为都是返回字符串列表,所以不需要自创对象了

java
@Mapper
@Repository
public interface 新嘉丽_质保SQL {
@Select("select 施工门店名称 from 质保模块.wf施工门店表")
List<String> 获取wf施工门店列表();
@Insert("insert into 质保模块.wf表 (wfid, 施工门店名称, 车辆品牌, 车型, 车架, 参考价格, 提交人, 提交日期) values (#{wfId}, #{施工门店名称}, #{车辆品牌}, #{车型}, #{车架}, #{参考价格}, #{提交人}, #{提交日期})")
void 录入wf质保(新嘉丽_新增质保对象 新增质保数据);
}
再创建两个API接口

java
@RestController
public class 新嘉丽_质保接口 {
@Autowired
private 新嘉丽_质保SQL 质保sql;
// API接口:获取可选门店列表
@GetMapping("/xinjiali_get_wf_stroe_list")
public Result xinjiali_get_wf_stroe_list(){
List<String> 施工门店列表 = 质保sql.获取wf施工门店列表();
return Result.success(施工门店列表);
}
// API接口:获取wf可选施工部位
@GetMapping("/xinjiali_get_wf_work_part_list")
public Result xinjiali_get_wf_work_part_list(){
List<String> 施工部位列表 = 质保sql.获取可选施工部位();
return Result.success(施工部位列表);
}
}
字符串列表返回到前端后,前端再进行数据处理
3. 服务器端-获取wfId接口
一开始打算前端写的,但是不太方便,加密函数比较难调用,所以就在后端写
为啥要用wfId呢,就是怕你前端连续点多几次提交/或者是网络发神经,点一次也会有多次请求,所以打开WF质保录入界面的时候,给你一个id编号,以防服务器录重复了质保,这个经验也是开发后才知道的
java
// API接口:获取wfId
@GetMapping("/xinjiali_get_wf_id")
public Result xinjiali_get_wf_id(){
String wfId = "wfId_" + 时间工具类.MD5时间戳().substring(0, 8);
return Result.success(wfId);
}
4. 服务器端-写入WF质保,并上传图片到阿里云OSS(重点)
因为表单是有字符串/列表外,还有图片,所以我这里采用的思路就是:先把json数据内容存入数据库,再把图片存入阿里云OSS,分开两个接口
接口1:正常存入常规json数据去PGSQL
下面是Object类
java
@Data
public class 新嘉丽_新增质保对象 {
private String wfId;
private String 施工门店名称;
private String 车辆品牌;
private String 车型;
private String 车架;
private String 车膜型号;
private List<String> 施工部位;
private String 施工时间;
private String 质保年限;
private String 车膜型号2;
private List<String> 施工部位2;
private LocalDate 施工时间2;
private String 质保年限2;
private String 车膜型号3;
private List<String> 施工部位3;
private LocalDate 施工时间3;
private String 质保年限3;
private BigDecimal 参考价格;
private String 图片url;
private LocalDate 提交日期;
private String 提交人;
下面是SQL语句
java
@Select("select wfid from 质保模块.wf表 where wfid=#{wfId}")
String 查询wfId是否存在(String wfId);
@Insert("insert into 质保模块.wf表 (wfid, 施工门店名称, 车辆品牌, 车型, 车架, 参考价格, 提交人, 提交日期, 提交时间) values (#{wfId}, #{施工门店名称}, #{车辆品牌}, #{车型}, #{车架}, #{参考价格}, #{提交人}, #{提交日期}, #{提交时间})")
void 录入wf质保(新嘉丽_新增质保对象 新增质保数据);
@Update("update 质保模块.wf表 set 车膜型号=#{车膜型号}, 施工时间=#{施工时间}::date, 质保年限=#{质保年限} where wfid=#{wfId}")
void 录入质保一信息(新嘉丽_新增质保对象 新增质保数据);
@Update("update 质保模块.wf表 set 施工部位=施工部位||ARRAY[#{单个施工部位}]::varchar[] where wfid=#{wfId}")
void 新增一个施工部位_1(String wfId, String 单个施工部位);
@Update("update 质保模块.wf表 set 车膜型号2=#{车膜型号2}, 施工时间2=#{施工时间2}::date, 质保年限2=#{质保年限2} where wfid=#{wfId}")
void 录入质保二信息(新嘉丽_新增质保对象 新增质保数据);
@Update("update 质保模块.wf表 set 施工部位2=施工部位2||ARRAY[#{单个施工部位}]::varchar[] where wfid=#{wfId}")
void 新增一个施工部位_2(String wfId, String 单个施工部位);
@Update("update 质保模块.wf表 set 车膜型号3=#{车膜型号3}, 施工时间3=#{施工时间3}::date, 质保年限3=#{质保年限3} where wfid=#{wfId}")
void 录入质保三信息(新嘉丽_新增质保对象 新增质保数据);
@Update("update 质保模块.wf表 set 施工部位3=施工部位3||ARRAY[#{单个施工部位}]::varchar[] where wfid=#{wfId}")
void 新增一个施工部位_3(String wfId, String 单个施工部位);
下面是API接口
java
/**
* API接口:新增wf质保
*/
@PostMapping("/xinjiali_add_wf")
@Transactional(rollbackFor = Exception.class) // 添加事务注解,设置回滚规则
public Result xinjiali_add_wf(@RequestHeader String jwt, @RequestBody 新嘉丽_新增质保对象 新增质保数据){
System.out.println(新增质保数据);
String openid = JwtUtils.get_openid(jwt);
// 拦截:wfId是否存在
if (质保sql.查询wfId是否存在(新增质保数据.getWfId()) != null){
return Result.error("wfId已存在");
}
// 先创建wfId
新增质保数据.set提交人(openid);
// 创建提交时间
新增质保数据.set提交时间(LocalDateTime.now());
质保sql.录入wf质保(新增质保数据);
/**
* 质保一
*/
// 新增车膜型号,施工时间,质保年限
质保sql.录入质保一信息(新增质保数据);
// 获取施工部位列表
List<String> 施工部位列表 = 新增质保数据.get施工部位();
// 遍历施工部位列表,每个施工部位都新增一个施工部位
for (String 单个施工部位 : 施工部位列表) {
质保sql.新增一个施工部位_1(新增质保数据.getWfId(), 单个施工部位);
}
/**
* 质保二
*/
// 新增车膜型号,施工时间,质保年限
质保sql.录入质保二信息(新增质保数据);
// 获取施工部位列表
List<String> 施工部位列表2 = 新增质保数据.get施工部位2();
// 遍历施工部位列表,每个施工部位都新增一个施工部位
for (String 单个施工部位 : 施工部位列表2) {
质保sql.新增一个施工部位_2(新增质保数据.getWfId(), 单个施工部位);
}
/**
* 质保三
*/
// 新增车膜型号,施工时间,质保年限
质保sql.录入质保三信息(新增质保数据);
// 获取施工部位列表
List<String> 施工部位列表3 = 新增质保数据.get施工部位3();
// 遍历施工部位列表,每个施工部位都新增一个施工部位
for (String 单个施工部位 : 施工部位列表3) {
质保sql.新增一个施工部位_3(新增质保数据.getWfId(), 单个施工部位);
}
return Result.success("新嘉丽_质保录入成功");
}
接口2:获取接口1返回成功后,调用接口2上传阿里云OSS并将url写入PGSQL
阿里云OSS是有官方文档的,有其他的一些功能,大家可以去看一下,需要到就自行配置
以下是阿里云OSS工具类,我用到多少就弄多少功能方法进去
java
@Component
public class 阿里云OSSUtils {
// 从容器对象里,获取参数
private String endpoint = "https://oss-cn-guangzhou.aliyuncs.com";
private String accessKeyId = "填你自己的";
private String accessKeySecret = "填你自己的";
private String bucketName = "xinjiali";
public String 阿里云OSS_上传质保图片(MultipartFile file, String 文件名) throws IOException {
// 获取上传文件的输入流
InputStream inputStream = file.getInputStream();
// 上传文件到阿里云OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, 文件名, inputStream);
// 文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + 文件名;
// 关闭OSSClient
ossClient.shutdown();
return url;
}
}
以下是SQL语句
java
@Update("update 质保模块.wf表 set 图片上传=#{图片URL} where wfid=#{wfId}")
void 新增WF质保图片(String wfId, String 图片URL);
以下是API接口
java
/**
* API接口:更新wf质保图片
*/
@PostMapping("/xinjiali_update_wf_image")
public Result xinjiali_update_wf_image(@RequestHeader String jwt, @RequestParam("wfId") String wfId, @RequestParam("file") MultipartFile 文件) {
/**
* 上传图片到阿里云OSS
*/
try {
// 上传文件到阿里云OSS
String 图片URL = 阿里云OSS_工具类.阿里云OSS_上传质保图片(文件, wfId + "_WF_image_1" + ".jpg");
// 新增图片URL到数据库
质保sql.新增WF质保图片(wfId, 图片URL);
return Result.success("新嘉丽_质保录入成功");
} catch (Exception e) {
e.printStackTrace();
return Result.error("上传图片到阿里云OSS失败");
}
}
5. 小程序端-跳转质保录入页面
在WF质保录入按钮,新增点击事件,并设置函数跳转到页面
XML
<script setup>
const goto = (value)=>{
switch(value){
case "质保录入":
uni.reLaunch({
url: '/pages/wf_enter/wf_enter'
})
break;
}
}
</script>
<div class="box" @click="goto('质保录入')">
<div class="icon"></div>
<div class="text">WF质保录入</div>
</div>
6. 小程序端-WF质保录入
实现代码如下,大家自己研究一下
XML
<script setup>
import { onMounted, ref } from 'vue';
let 可选_wf施工门店列表 = ref([])
let 可选_wf施工部位 = ref([])
let jwt = uni.getStorageSync('jwt')
let Data = ref({
// 测试样例,用于方便调试
"wfId": '',
"施工门店名称": "",
"车辆品牌":'',
'车型': "",
"车架": ""
})
// 预览图片
const previewImage = ()=>{
uni.previewImage({
urls: [Data.value.图片], // 需要预览的图片链接数组
current: Data.value.图片 // 当前显示的图片链接
});
}
// 选择图片
const chooseImage = ()=>{
uni.chooseImage({
count: 1, // 只上传一个照片就可以了
sizeType: ['original','compressed'],
sourceType: ['album','camera'],
success: (res) => {
Data.value.图片 = res.tempFilePaths[0] // 临时url
uni.showToast({
title: '选择图片成功',
icon: 'success'
})
}
})
}
const add_wf = ()=>{
uni.request({
url: uni.$url+'/xinjiali_add_wf',
method: 'POST',
header:{jwt:jwt},
data: Data.value,
success: (res) => {
console.log("⬇️新增wf质保返回")
console.log(res)
console.log(res.data.code)
if(res.data.code===1){
// 再上传
uni.uploadFile({
url: uni.$url+'/xinjiali_update_wf_image',
filePath: Data.value.图片,
name: 'file',
formData: {"wfId": Data.value.wfId},
header: {
'content-type': 'multipart/form-data' , // 固定格式,必须加
jwt: jwt
},
success: (res) => {
console.log("👇新增WF的结果")
console.log(res)
if(res.data.code===1){
uni.reLaunch({
url: '/pages/my/my' // 先直接回到最外面个人页面,以后有需要再改
})
}
}
})
}
else{
uni.showToast({
title: res.data.msg,
icon: 'error'
})
}
}
})
}
// 获取wf的可选施工门店列表与可选施工部位
const get_wf_store_and_word_part = ()=>{
uni.request({
url: uni.$url+"/xinjiali_get_wf_stroe_list",
method: 'GET',
success: (res) => {
console.log("获取wf施工门店列表⬇️")
console.log(res)
可选_wf施工门店列表.value = uni.$change_Arr_same(res.data.data)
}
})
uni.request({
url: uni.$url+"/xinjiali_get_wf_work_part_list",
method: 'GET',
success: (res) => {
console.log("获取可选wf施工部位")
console.log(res)
可选_wf施工部位.value = uni.$change_Arr_same(res.data.data)
}
})
uni.request({
url: uni.$url+'/xinjiali_get_wf_id',
method: 'GET',
header:{jwt:jwt},
success: (res) => {
console.log("⬇️获取wfId")
console.log(res)
Data.value.wfId = res.data.data
}
})
}
onMounted(()=>{
// 获取wf施工门店列表与wf施工部位
get_wf_store_and_word_part()
})
</script>
<template>
<view class="page">
<uni-section class="mb-10" title="施工门店名称" type="line" ></uni-section>
<div class="xianzhi">
<uni-data-select v-model="Data.施工门店名称" :localdata="可选_wf施工门店列表" ></uni-data-select>
</div>
<uni-section class="mb-10" title="车辆品牌" type="line" ></uni-section>
<input class="custom-input" type="text" placeholder="请输入" v-model="Data.车辆品牌"/>
<uni-section class="mb-10" title="车型" type="line" ></uni-section>
<input type="text" placeholder="请输入" v-model="Data.车型"/>
<uni-section class="mb-10" title="车架号" type="line" ></uni-section>
<input type="text" placeholder="请输入车架号,查询使用" v-model="Data.车架"/>
<!-- 1 -->
<uni-section class="mb-10" title="1. 车膜型号" type="line" title-color="#f18407"></uni-section>
<input type="text" placeholder="请输入" v-model="Data.车膜型号"/>
<uni-section class="mb-10" title="1. 施工部位" type="line" title-color="#f18407"></uni-section>
<div class="xianzhi">
<uni-data-select multiple v-model="Data.施工部位" :localdata="可选_wf施工部位" ></uni-data-select>
</div>
<uni-section class="mb-10" title="1. 施工时间" type="line" title-color="#f18407"></uni-section>
<div class="xianzhi">
<uni-datetime-picker type="date" :clear-icon="false" v-model="Data.施工时间" @maskClick="maskClick" />
</div>
<uni-section class="mb-10" title="1. 质保年限" type="line" title-color="#f18407"></uni-section>
<input type="text" placeholder="请输入" v-model="Data.质保年限"/>
<!-- 2 -->
<uni-section class="mb-10" title="2. 车膜型号" type="line" title-color="#f16553"></uni-section>
<input type="text" placeholder="请输入" v-model="Data.车膜型号2"/>
<uni-section class="mb-10" title="2. 施工部位" type="line" title-color="#f16553"></uni-section>
<div class="xianzhi">
<uni-data-select multiple v-model="Data.施工部位2" :localdata="可选_wf施工部位" ></uni-data-select>
</div>
<uni-section class="mb-10" title="2. 施工时间" type="line" title-color="#f16553"></uni-section>
<div class="xianzhi">
<uni-datetime-picker type="date" :clear-icon="false" v-model="Data.施工时间2" @maskClick="maskClick" />
</div>
<uni-section class="mb-10" title="2. 质保年限" type="line" title-color="#f16553"></uni-section>
<input type="text" placeholder="请输入" v-model="Data.质保年限2"/>
<!-- 3 -->
<uni-section class="mb-10" title="3. 车膜型号" type="line" title-color="#0a47b8"></uni-section>
<input type="text" placeholder="请输入" v-model="Data.车膜型号3"/>
<uni-section class="mb-10" title="3. 施工部位" type="line" title-color="#0a47b8"></uni-section>
<div class="xianzhi">
<uni-data-select multiple v-model="Data.施工部位3" :localdata="可选_wf施工部位" ></uni-data-select>
</div>
<uni-section class="mb-10" title="3. 施工时间" type="line" title-color="#0a47b8"></uni-section>
<div class="xianzhi">
<uni-datetime-picker type="date" :clear-icon="false" v-model="Data.施工时间3" @maskClick="maskClick" />
</div>
<uni-section class="mb-10" title="3. 质保年限" type="line" title-color="#0a47b8"></uni-section>
<input type="text" placeholder="请输入" v-model="Data.质保年限3"/>
<!-- 最后收尾 -->
<uni-section class="mb-10" title="参考价格" type="line" ></uni-section>
<input type="text" placeholder="请输入" v-model="Data.参考价格"/>
<uni-section class="mb-10" title="图片上传" type="line" ></uni-section>
<image :src="Data.图片" mode="widthFix" class="preview_img" @click="previewImage"></image>
<div style="display: flex;justify-content: space-between;">
<button type="primary" @click="chooseImage">选择图片</button>
<button type="primary" @click="Data.图片=''">删除图片</button>
</div>
<uni-section class="mb-10" title="提交日期" type="line" ></uni-section>
<uni-datetime-picker type="date" :clear-icon="false" v-model="Data.提交日期" @maskClick="maskClick" />
<button type="warn" @click="add_wf" style="margin: 30px 30px;">提交</button>
</view>
</template>
<style lang="less">
page{
padding-bottom: 30px; // 用来给提交按钮腾位置,使空间宽松一点
}
.xianzhi{
margin-left: 20px;
margin-right: 20px;
width: 89 %;
}
.preview_img{
width: 150px;
height: 100px;
background-size: contain;
background-repeat: no-repeat;
}
input{
// margin-left: 20px;
// border: 1px solid #888;
// padding-right: 20px;
height: 40px;
margin: 0 20px;
background-color: #d9d9d9;
padding-left: 20px;
font-size: 13px;
}
.uni-section{
margin-top: 20px;
// margin: 0 20px;
}
.page{
padding-left: 10px;
padding-right: 10px;
}
</style>