智慧门店系统开发-06-新嘉丽WF质保录入功能实现

目录

[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>
相关推荐
小北方城市网1 天前
GEO 新生态:跨界融合 + 场景渗透,重构 AI 时代本地商业增长版图
大数据·网络·人工智能·python·状态模式
小园子的小菜1 天前
Token自动续期技术方案全解析:原理、优缺点与生产实践
java·后端·架构·状态模式
小北方城市网2 天前
第 4 课:前端工程化进阶 ——Vue 核心语法 + 组件化开发(前端能力质的飞跃)
大数据·开发语言·数据库·python·状态模式·数据库架构
山风wind4 天前
设计模式:状态模式详解-让对象的行为随状态改变而改变
设计模式·状态模式
hdsoft_huge5 天前
Java 实现高效查询海量 geometry 及 Protobuf 序列化与天地图前端分片加载
java·前端·状态模式
巾帼前端5 天前
前端对用户因果链的优化
前端·状态模式
有一个好名字6 天前
设计模式-状态模式
设计模式·状态模式
想学后端的前端工程师7 天前
【微前端架构实战指南:从原理到落地】
前端·架构·状态模式
熊猫钓鱼>_>7 天前
GLM4.6多工具协同开发实践:AI构建智能任务管理系统的完整指南
人工智能·python·状态模式·ai编程·glm·分类系统·开发架构