刘一辰的软件工程随笔


软件构造实验作业

班级:信1905-2学号:20193897姓名:刘一辰

一、实验要求

根据参考资料,学习Guns框架的使用并如下任务:

任务一:导入并配置Guns框架

任务二:阅读Gans的源码并对每一部分的功能进行介绍

任务三:基于Gans完成一个汽车信息管理系统

二、实验步骤

任务一:导入并配置Guns框架

首先在Gitee下载项目

打开之后不着急运行项目,先打开idea -> file -> settings,导入maven配置

库名要和数据库的库名一致,如果数据库没有这个库的话需要创建。

数据库内容不用初始化,因为项目启动后会自动初始化表。之后运行,Guns的启动类即可

打开 http://localhost:8080 (opens new window), 输入默认账号密码:admin/123456,即可进入系统。

任务二:阅读Gans的源码并对每一部分的功能进行介绍

学习一下 github 上面用 Tensorflow 写的 GAN 代码。

dataset.py

import multiprocessing # 允许程序员充分利用多个核心

import tensorflow as tf # 导入 tensorflow

 

def make_anime_dataset(img_paths,batch_size,resize=64,drop_remainder=True,shuffle=True,repeat=1): 

    # 生成动漫数据集(图片路径, 批量大小, ...)

    @tf.function

Tip: 函数装饰器

 

# 用函数 function1 去装饰函数 function2

 

def function1(function): # 函数 function1 的形式参数传入一个函数

    # ...

    function()

    return A

 

@function1

def function2():

    # ...

 

# 上面的代码等价于下面的代码

function2=function1(function2)

# 相当于用函数 function1 处理了一下 function2

Tip: tf.function

TensorFlow 2 中,只需要将我们希望以图执行模式(Graph Execution)/ 而非及时执行模式(Eager Execution)的代码写成一个函数,并在函数前加上@tf.function 进行装饰就可以了。

 

def make_anime_dataset(img_paths, batch_size, resize=64, drop_remainder=True, shuffle=True, repeat=1):  # img_paths 是一个数组

    @tf.function # 下面函数内的代码以图执行模式执行

    def _map_fn(img):

        img = tf.image.resize(img, [resize, resize]) # 将图片大小变为 64x64

        img = tf.clip_by_value(img,0,255) # 将张量 img 中的数值限制在 0~255 之内

        img = img / 127.5 - 1 # 将图片的值归一化为 -1 ~ 1(猜测后面的会用 tanh 作为激活函数)

        return img # 返回归一化处理后的图片张量

   

    dataset = disk_imag_batch_dataset(img_paths,

                                      batch_size,

                                      drop_remainder=drop_remainder,

                                      map_fn=map_fn,

                                      shuffle=shuffle,

                                      repeat=repeat)

    img_shape = (resize, resize, 3) # 图片的张量形状 (64x64x3) 3 个通道为 RGB 三个通道

    len_dataset = len(img_paths) // batch_size  # 有多少个 batch // 表示整除

   

    return dataset, img_shape, len_dataset

 

def batch_dataset(dataset,

                  batch_size,

                  drop_remainder=True,

                  n_prefetch_batch=1,

                  flter_fn=None,

                  n_map_threads=None,

                  filter_after_map=False,

                  shuffle=True,

                  shuffle_buffer_size=None,

                  repeat=None):

    # 对数据集进行 洗牌、筛选、处理、打包

    if n_map_threads is None: # 如果参数 n_map_threads 传入值为 None

        n_map_threads = multiprocessing.cpu_count() # 返回 cpu 的核数 目前 cpu 应该都是 4 核的吧,老一点的 cpu 有 2 核的

    if shuffle and shuffle_buffer_size is None: # 如果这两个参数其中一个是 None

        shuffle_buffer_size = max(batch_size*128,2048) # 最小的用于打乱的 buffer_size 是2048,其他的是批量大小的 128 倍

   

    # 在对 dataset 执行 map 之前,先执行 shuffle 是更有效率的,因为 map 有时很耗时间

    if shuffle: # 如果需要打乱

        dataset=dataset.shuffle(shuffle_buffer_size) # 对数据集进行打乱

   

    if not filter_after_map:

        if filter_fn:

            dataset = dataset.filter(filter_fn) # 对数据集进行过滤,过滤标准依照函数filter_fn

           

        if map_fn:

            dataset = dataset.map(map_fn, num_parallel_calls=n_map_threads) # 对数据集中的所有数据进行操作,多线程并行操作      

    else:  # 如果不需要过滤

        if map_fn:

            dataset = dataset.map(map_fn, num_parallel_calls=n_map_threads)

       

        if filter_fn:

            dataset = dataset.filter(filter_fn)

    dataset = dataset.batch(batch_size, drop_remainder=drop_remainder) # 将数据集分成一个一个小块, drop_remainder:对于最后一个不足 batch_size 的数据是保留还是删除,默认选择保留。

    dataset = dataset.repeat(repeat).prefetch(n_prefetch_batch) # prefetch 放在最后提供一个 software pipelining 机制。使得 GPU 在训练上一次准备的数据时,CPU 去准备下一次需要的数据。如果单个训练步骤消耗 n 个元素,则添加 prefetch(n)。

    return dataset

 

def memory_data_batch_dataset(memory_data, #list/ndarray/Tensor

                              batch_size,

                             drop_remainder=True,

                             n_prefectch_batch=1,

                             filter_fn=None,

                             map_fn=None,

                             n_map_threads=None,

                             filter_after_map=False,

                             shuffle_buffer_size=None,

                             repeat=None):

    # 从内存中读取数据、制作数据集

    dataset = tf.data.Dataset.from_tensor_slices(memory_data) # 将 memory_data 制作成数据集

    dataset = batch_dataset(dataset,

                            batch_size,

                           drop_remainder=drop_remainder,

                           n_prefetch_batch=n_prefetch_batch,

                           filter_fn=filter_fn,

                           map_fn=map_fn,

                           n_map_threads=n_map_threads,

                           filter_after_map=filter_after_map,

                           shuffle=shuffle,

                           shuffle_buffer_size=shuffle_buffer_size,

                           repeat=repeat) # 对 dataset 洗牌、筛选、处理、打包

    return dataset

 

def disk_image_batch_dataset(img_paths,

                            batch_size,

                            labels=None,

                            drop_remainder=True,

                            n_prefetch_batch=1,

                            filter_fn=None,

                            map_fn=None,

                            n_map_threads=None,

                            filter_after_map=False,

                            shuffle=True,

                            shuffle=buffer_size=None,

                            repeat=None):

    # 从磁盘数据中读取数据制作数据集

    if label is None:

        memory_data = img_paths # 如果没有标签

    else:

        memory_data = (img_patchs,labels) # 如果有标签

   

    def parse_fn(path, *label): # * 表示接受一个元组、** 表示接受一个字典

        # 读取文件 返回一个元组

        img = tf.io.read_file(path) # 读取文件

        img = tf.image.decode_png(img,3) # 解码 png 图片 通道数固定到 RGB 三个通道

        return (img,) + label # 合成一个元组

   

    if map_fn: # 融合 map_fn 和 parse_fn 如果 map_fn 有传入值

        def map_fn_(*args):

            return map_fn(*parse_fn(*args)) # 对图片进行处理

    else:

        map_fn_ = parse_fn # 将 map_fn 和 parse_fn合成为一个函数 map_fn_

   

    # 从磁盘中读取数据,其实本质上也是把磁盘中的数据读取到内存中,最后从内存中制作数据集

    dataset = memory_data_batch_dataset(memory_data,

                                       batch_size,

                                       drop_remainder=drop_remainder,

                                       n_prefetch_batch=n_prefetch_batch,

                                       filter_fn=filter_fn,

                                       map_fn=map_fn_,

                                       n_map_threads=n_map_threads,

                                       filter_after_map=filter_after_map,

                                       shuffle=shuffle,

                                       shuffle_buffer_size=shuffle_buffer_size,

                                       repeat=repeat)

    return dataset

gan.py

import os # 导入 os 系统模块

import tensorflow as tf # 导入 tensorflow 模块

from tensorflow import keras # 导入 keras 模型部分

 

os.environp['TF_CPP_MIN_LOG_LEVEL']='2' # 屏蔽 INFO 和 WARNING 输出 ERRPR 和 FATAL

class Generator(keras.Model):

    def __init__(self):

        # 显示确定一些需要的参数、其实可以不用在__init__中定义,直接在 call 中使用即可。

        super(Generator,self).__init__() # 向父类注册

        self.fc=keras.layers.Dense(3*3*512)

       

        self.conv1=keras.layers.Conv2DTranspose(256,3,3,'valid') # 反卷积 kernel_size=3 filters=256,padding='valid'

        self.bn1=keras.layers.BatchNormalization()

        self.conv2=keras.layers.Conv2DTranspose(128,5,2,'valid')

        self.b2=keras.layers.BatchNormalization()

       

        self.conv3=keras.layers.Conv2DTranspose(3,4,3,'valid')

   

    def call(self, inputs, training=None, mask=None):

        x=self.fc(inputs)

        x=tf.reshape(x,[-1,3,3,512])

        x=tf.nn.leaky_relu(x)

       

        x=self.conv1(x)

        x=self.bn1(x,training=training)

        x=tf.nn.leaky_relu(x)

      

        x=self.conv2(x)

        x=self.bn2(x,training=training)

        x=tf.nn.leaky_relu(x)

        

        x=self.conv3(x)

        x=tf.tanh(x)

        return x

        # 这个例子告诉我们,在 keras 的自定义层中直接在 call 中调用 keras 内置层是可以有可训练变量的。

       

 class Discriminator(keras.Model):

    def __init__(self):

        super(Discriminator,self).__init__()

        self.conv1=keras.layers.Conv2D(64,5,3,'valid')

       

        self.conv2=keras.layers.Conv2D(128,5,3,'valid')

        self.bn2=keras.layers.BatchNormalization()

       

        self.conv3=keras.layers.BatchNormalization()

        self.bn3=keras.layers.BatchNormalization()

       

        self.flattn=keras.layers.Flatten()

        self.fc=keras.layers.Dense(1)

       

    def call(self, inputs, training=None, mask=None):

        x=self.conv1(inputs)

        x=tf.nn.leaky_relu(x)

        x=self.conv2(x)

        x=self.bn2(x,training=training)

        x=tf.nn.leaky_relu(x)

        x=self.conv3(x)

        x=self.bn3(x,training=training)

        x=tf.nn.leaky_relu(x)

       

        x=self.flatten(x)

        logits=self.fc(x)

       

        return logits

Tip: keras 两种初始化模型的方法

 

原来我只会第一种初始化模型的方法,这份源码教会了我第二种方法

 

# 方法1:从 Input 开始指定前向过程,最后根据输入和输出来建立模型

inputs = tf.keras.Input(shape=(3,))

x = tf.keras.layers.Dense(4, activation=tf.nn.relu)(inputs)

outputs = tf.keras.layers.Dense(5, activation=tf.nn.softmax)(x)

model = tf.keras.Model(inputs=inputs, outputs=outputs)

 

# 方法2:构建 Model 的子类,__init__中定义层的实现,call函数中实现前向过程

 

class MyModel(tf.keras.Model):

 

    def __init__(self):

        super(MyModel, self).__init__()

        self.dense1 = tf.keras.layers.Dense(4, activation=tf.nn.relu)

        self.dense2 = tf.keras.layers.Dense(5, activation=tf.nn.softmax)

       

    def call(self, inputs):

        x = self.dense1(inputs)

        output = self.dense2(2)

        return output

   

model = MyModel()    # 通过初始化整个类来初始化该模型

def main():

    d=Discrimination()

    g=Generator()

   

    x=tf.random.normal([2,64,64,3])

    z=tf.random.normal([2,100]) # 随机初始化两个一百维度的 z

   

    prob=d(x)

    print(prob.shape) # 应该的形状为[-1,1]

    x_hat=g(z)

    print(x_hat.shape) # 应该的形状为[-1,64,64,3]

if __name__=='__main__': # 这样写 在作为一个模块导入其他文件时 main() 并不会被直接执行因为在其他文件调用此文件时,__name__!='__main__',而如果直接运行本模块的化,可以直接运行 main 函数此时 __name__=='main'

    main()

gan_train.py

import os

import numpy as np

import tensorflow as tf

from tensorflow import keras

form PIL import Image # PIL 是图像处理标准库

import glob # glob 是操作文件的相关模块

from gan import Generator, Discriminator

 

from dataset import make_anime_dataset

 

def save_result(val_out, val_block_size, image_path, color_mode):

    # 保存结果

    def prepocess(img):

        img = ((img+1.0)*127.5).astype(np.uint8) # 将输出的张量复原为一张图像

        return img

   

    preprocesed = prepocess(val_out)

    final_image = np.array([])

    single_row = np.array([])

    for b in range(val_out.shape[0]):

        if single_row.size == 0: # 如果 single_row 中没有图片

            single_row = preprocesed[b,:,:,:] # 取第 b 张图片

        else: # 如果 single_row 中已经有图片了

            single_row = np.concatenate((single_row,preprocesed[b,:,:,:]),axis=1) # 将图片和之前的图片沿着 axis=1轴 合并成一张图片

       

        if (b+1) % val_block_size == 0: # 如果这是最后一张图片

            if final_image.size == 0:

                final_image = single_row # 将 final_image 令为最后一张图片

            else:

                final_image = np.concatenate((final_image, single_row), axis=0) # 如果 final_image 中有图片就再在其中添加 single_row

           

            single_row = np.array([]) # 重置 single_row

   

    if final_image.shape[2]==1:

        final_image=np.squeeze(final_image,axis=2)

    Image.fromarray(final_image).save(image_path)

   

 

def celoss_ones(logits): # logits shape (None,1)

    # 与全 1 张量之间的交叉熵

    loss = tf.nn.sigmoid_cross_entropy_with_logits(logits=logits,

                                                   labels=tf.ones_like(logits)) # tf.zeros_like/tf.ones_like 新建一个与给定 tensor 类型大小一致的 tensor 所有元素为 0/1 .

    return tf.reduce_mean(loss)

 

 

def celoss_zeros(logits): # logits shape (None,1)

    # 与全 0 张量之间的交叉熵

    loss = tf.nn.sigmoid_cross_entropy_with_logits(logits=logits,

                                                   labels=tf.zeros_like(logits)) # tf.zeros_like/tf.ones_like 新建一个与给定 tensor 类型大小一致的 tensor 所有元素为 0/1 .

    return tf.reduce_mean(loss)

 

 

def d_loss_fn(generator, discriminator, batch_z, batch_x, is_training):                             

    # discriminator 的损失函数

    fake_image = generator(batch_z, is_training)

    d_fake_logits = discriminator(fake_image, is_training)

    d_real_logits = discriminator(batch_x, is_training)

   

    d_loss_real = celoss_ones(d_real_logits)

    d_loss_fake = celoss_zeros(d_fake_logits)

   

    loss = d_loss_fake + d_loss_real # 注意 fake 和 real 是等比例输入的,之后 discriminator 的 loss 应该将两部分加起来。

    

    return loss

 

def g_loss_fn(generator, discriminator, batch_z, is_training):

    # generator 的损失函数

    fake_image = generator(batch_z, istraining) # 相当于一个 layer (batch_z 是该 layer 的输入)

    d_fake_logits = discriminator(fake_image,is_training)

    loss = celoss_ones(d_fake_logits)

   

    return loss

 

def main():

   

    tf.random.set_seed(22) # 设置随机变量种子

    np.random.seed(22)

    os.environ['TF_CPP_MIN_LOG_LEVEL']='2' # ERROR 及其以上的错误才报

    assert tf.__version__.startswith('2.') # 一定要是 tensorflow 2.0

Tip: assert

 

assert expression # 声称,如果不是的话就抛出错误

 

# 等价于:

 

if not expression:

    raise AssertionError

   

def main():

    tf.random.set_seed(22) # 设置随机变量种子

    np.random.seed(22)

    os.environ['TF_CPP_MIN_LOG_LEVEL']='2' # ERROR 及其以上的错误才报

    assert tf.__version__.startswith('2.') # 一定要是 tensorflow 2.

   

    z_dim = 100

    epochs = 3000000

    batch_size = 512

    learning_rate = 0.002

    is_training = True

   

    img_path=glob.glob(r'E:\python_pro\TF2.0\GAN\faces\*.jpg') #图片路径

    dataset, img_shape, _ = make_anime_dataset(img_path, batch_size)

    print(dataset, img_shape)

    dataset = dataset.repeat()

    db_iter = iter(dataset) # iter(object)函数用来生成迭代器,其中 object 为支持迭代的集合对象,返回一个迭代器对象

   

    generator = Generator()

    generator.build(input_shape=(None, z_dim))

    discriminator = Discriminator()

    discriminator.build(input_shape=(None, 64, 64, 3))

   

    g_optimizer = tf.optimizers.Adam(learning_rate=learning_rate, beta_1=0.5)

    d_optimizer = tf.optimizers.Adam(learning_rate=learning_rate, beta_1=0.5)

   

    for epoch in range(epochs): # 每一个训练周期

       

        batch_z = tf.random.uniform([batch_size,z_dim],minval=-1.,maxval=1.)

        batch_x = next(db_iter) # 每个周期只会取其中的一个小 batch 来训练

Tip:以前我每个 epoch 选取 batch 的写法

 

for epoch in range(epochs):

    for data, label in dataset:

没有什么太大的区别,它的一个周期取一个 batch 我的一个周期 遍历整个数据集。

# 发现一个新大陆,所有的Dataset 可以用 iter(Dataset) 生成迭代对象,使用 next 取出。相当于一个栈式结构,每次取出一个 tf.Tensor.

db_iter=iter(dataset)

for epoch in range(epochs):

    batch_x=next(db_iter)

 

        with if.GradientTape() as tape:

            d_loss = d_loss_fn(generator, discriminator, batch_z, batch_x, is_training)

            grads = tape.gradient(d_loss,discriminator.trainable_variables) 

            d_optimizer.apply_gradients(zip(grads, discriminator.trainable_variables))

           

        with tf.GradientTape() as tape:

            g_loss = g_loss_fn(generator, discriminator, batch_z, is_training)

            grads = tape.gradient(g_loss,generator.trainable_variables)

            g_optimizer.apply_gradients(zip(grads, generator.trainable_variables))

       

        if epoch % 100 == 0:

            print(epoch,'d-loss:',float(d_loss),'g-loss',float(g_loss))

           

            z = tf.random.uniform([100,z_dim])

            fake_image = generator(z,training=False)

            img_path = os.path.join('images','gan-%d.png'%epoch)

            save_result(fake_image.numpy(),10,img_path,color_mode='P')

if __name__=='__main__':

                                    main()

学学别人写的源码也是挺好的。

任务三:基于Gans完成一个汽车信息管理系统

根据gans快速开发平台给出的示例,开始完成这个汽车信息管理系统

首先准备好初始化的sql,初始化到数据库中

DROP TABLE IF EXISTS `car`;

CREATE TABLE `car`  (

  `car_id` bigint(20) NOT NULL COMMENT '车辆id',

  `car_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '车辆名称',

  `car_type` tinyint(4) NULL DEFAULT NULL COMMENT '车辆种类:1-轿车,2-货车',

  `car_color` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '车辆颜色',

  `car_price` decimal(20, 2) NULL DEFAULT NULL COMMENT '车辆价格',

  `manufacturer` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '制造商',

  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',

  `create_user` bigint(20) NULL DEFAULT NULL COMMENT '创建人',

  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',

  `update_user` bigint(20) NULL DEFAULT NULL COMMENT '更新人',

  PRIMARY KEY (`car_id`) USING BTREE

) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '车辆管理' ROW_FORMAT = Dynamic;

 

INSERT INTO `car` VALUES (1339554696976782409, '奥迪A6', 1, '白色', 300000.00, '奥迪公司', '2021-02-06 17:06:33', NULL, NULL, NULL);

INSERT INTO `car` VALUES (1339554696976782410, '一汽解放', 2, '黑色', 200000.00, '一汽公司', '2021-02-06 17:06:33', NULL, NULL, NULL);

因此,我们可以在前端顶层中得到一个业务应用APP里面的车辆管理系统

 

并且得到了六个文件

其中包括

三个jar包

 

三个前端页面

 

然后是后端

这是后端目录

 

项目内容:

CarController

package cn.stylefeng.guns.modular.business.controller;

 

import cn.stylefeng.guns.modular.business.pojo.CarRequest;

import cn.stylefeng.guns.modular.business.service.CarService;

import cn.stylefeng.roses.kernel.rule.pojo.response.ResponseData;

import cn.stylefeng.roses.kernel.rule.pojo.response.SuccessResponseData;

import cn.stylefeng.roses.kernel.scanner.api.annotation.ApiResource;

import cn.stylefeng.roses.kernel.scanner.api.annotation.GetResource;

import cn.stylefeng.roses.kernel.scanner.api.annotation.PostResource;

import org.springframework.validation.annotation.Validated;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RestController;

 

import javax.annotation.Resource;

 

/**

 * 车辆管理控制器

 *

 * @author fengshuonan

 * @date 2020/3/25 14:00

 */

@RestController

@ApiResource(name = "车辆管理")

public class CarController {

 

    @Resource

    private CarService carService;

 

    /**

     * 添加车辆

     *

     * @author fengshuonan

     * @date 2020/3/25 14:00

     */

    @PostResource(name = "添加车辆", path = "/car/add")

    public ResponseData add(@RequestBody @Validated(CarRequest.add.class) CarRequest carRequest) {

        carService.add(carRequest);

        return new SuccessResponseData();

    }

 

    /**

     * 删除车辆

     *

     * @author fengshuonan

     * @date 2020/3/25 14:00

     */

    @PostResource(name = "删除车辆", path = "/car/delete")

    public ResponseData delete(@RequestBody @Validated(CarRequest.delete.class) CarRequest carRequest) {

        carService.del(carRequest);

        return new SuccessResponseData();

    }

 

    /**

     * 编辑车辆

     *

     * @author fengshuonan

     * @date 2020/3/25 14:00

     */

    @PostResource(name = "编辑车辆", path = "/car/edit")

    public ResponseData edit(@RequestBody @Validated(CarRequest.edit.class) CarRequest carRequest) {

        carService.edit(carRequest);

        return new SuccessResponseData();

    }

 

    /**

     * 查看车辆详情

     *

     * @author fengshuonan

     * @date 2020/3/25 14:00

     */

    @GetResource(name = "查看车辆详情", path = "/car/detail")

    public ResponseData detail(@Validated(CarRequest.detail.class) CarRequest carRequest) {

        return new SuccessResponseData(carService.detail(carRequest));

    }

 

    /**

     * 查询车辆列表

     *

     * @author fengshuonan

     * @date 2020/3/25 14:00

     */

    @GetResource(name = "获取车辆列表", path = "/car/findList")

    public ResponseData list(CarRequest carRequest) {

        return new SuccessResponseData(carService.findList(carRequest));

    }

 

    /**

     * 分页查询车辆列表

     *

     * @author fengshuonan

     * @date 2020/3/25 14:00

     */

    @GetResource(name = "分页查询", path = "/car/findPage")

    public ResponseData page(CarRequest carRequest) {

        return new SuccessResponseData(carService.findPage(carRequest));

    }

 

}

CarViewController

package cn.stylefeng.guns.modular.business.controller;

 

import cn.stylefeng.roses.kernel.scanner.api.annotation.ApiResource;

import cn.stylefeng.roses.kernel.scanner.api.annotation.GetResource;

import org.springframework.stereotype.Controller;

 

/**

 * 车辆管理界面

 *

 * @author fengshuonan

 * @date 2020/3/25 14:00

 */

@Controller

@ApiResource(name = "车辆管理界面")

public class CarViewController {

 

    /**

     * 车辆管理首页

     *

     * @author fengshuonan

     * @date 2020/3/25 14:00

     */

    @GetResource(name = "车辆管理首页", path = "/view/car")

    public String carIndex() {

        return "/modular/business/car/car.html";

    }

 

    /**

     * 车辆管理-新增

     *

     * @author fengshuonan

     * @date 2020/3/25 14:00

     */

    @GetResource(name = "车辆管理-新增", path = "/view/car/add")

    public String carAdd() {

        return "/modular/business/car/car_add.html";

    }

 

    /**

     * 车辆管理-编辑

     *

     * @author fengshuonan

     * @date 2020/3/25 14:00

     */

    @GetResource(name = "车辆管理-编辑", path = "/view/car/edit")

    public String carEdit() {

        return "/modular/business/car/car_edit.html";

    }

 

}

Car

package cn.stylefeng.guns.modular.business.entity;

 

import cn.stylefeng.roses.kernel.db.api.pojo.entity.BaseEntity;

import com.baomidou.mybatisplus.annotation.TableField;

import com.baomidou.mybatisplus.annotation.TableId;

import com.baomidou.mybatisplus.annotation.TableName;

import lombok.Data;

import lombok.EqualsAndHashCode;

 

import java.math.BigDecimal;

 

/**

 * 车辆管理

 *

 * @author stylefeng

 * @date 2020/3/25 14:00

 */

@TableName("car")

@EqualsAndHashCode(callSuper = true)

@Data

public class Car extends BaseEntity {

 

    /**

     * 车辆id

     */

    @TableId("car_id")

    private Long carId;

 

    /**

     * 车辆名称

     */

    @TableField("car_name")

    private String carName;

 

    /**

     * 车辆种类:1-轿车,2-货车

     */

    @TableField("car_type")

    private Integer carType;

 

    /**

     * 车辆颜色

     */

    @TableField("car_color")

    private String carColor;

 

    /**

     * 车辆价格

     */

    @TableField("car_price")

    private BigDecimal carPrice;

 

    /**

     * 制造商

     */

    @TableField("manufacturer")

    private String manufacturer;

 

}

CarExceptionEnum

package cn.stylefeng.guns.modular.business.exception;

 

import cn.stylefeng.guns.core.consts.ProjectConstants;

import cn.stylefeng.roses.kernel.rule.constants.RuleConstants;

import cn.stylefeng.roses.kernel.rule.exception.AbstractExceptionEnum;

import lombok.Getter;

 

/**

 * 车辆异常枚举

 *

 * @author fengshuonan

 * @date 2020/3/25 14:00

 */

@Getter

public enum CarExceptionEnum implements AbstractExceptionEnum {

 

    /**

     * 车辆不存在

     */

    CAR_NOT_EXISTED(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + ProjectConstants.BUSINESS_EXCEPTION_STEP_CODE + "01", "车辆不存在");

 

    /**

     * 错误编码

     */

    private final String errorCode;

 

    /**

     * 提示用户信息

     */

    private final String userTip;

 

    CarExceptionEnum(String errorCode, String userTip) {

        this.errorCode = errorCode;

        this.userTip = userTip;

    }

 

}

CarMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>

 

CarMapper

package cn.stylefeng.guns.modular.business.mapper;

 

import cn.stylefeng.guns.modular.business.entity.Car;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

 

/**

 * 车辆管理数据层

 *

 * @author stylefeng

 * @date 2020/3/25 14:00

 */

public interface CarMapper extends BaseMapper {

}

CarRequest

package cn.stylefeng.guns.modular.business.pojo;

 

import cn.stylefeng.roses.kernel.rule.pojo.request.BaseRequest;

import lombok.Data;

import lombok.EqualsAndHashCode;

 

import javax.validation.constraints.NotBlank;

import javax.validation.constraints.NotNull;

import java.math.BigDecimal;

 

/**

 * 车辆管理请求

 *

 * @author stylefeng

 * @date 2020/3/25 14:00

 */

@EqualsAndHashCode(callSuper = true)

@Data

public class CarRequest extends BaseRequest {

 

    /**

     * 车辆id

     */

    @NotNull(message = "车辆id不能为空", groups = {edit.class, delete.class, detail.class})

    private Long carId;

 

    /**

     * 车辆名称

     */

    @NotBlank(message = "车辆名称不能为空", groups = {add.class, edit.class})

    private String carName;

 

    /**

     * 车辆种类:1-轿车,2-货车

     */

    @NotNull(message = "车辆种类不能为空", groups = {add.class, edit.class})

    private Integer carType;

 

    /**

     * 车辆颜色

     */

    private String carColor;

 

    /**

     * 车辆价格

     */

    private BigDecimal carPrice;

 

    /**

     * 制造商

     */

    private String manufacturer;

 

}

CarServiceImpl

package cn.stylefeng.guns.modular.business.service.impl;

 

import cn.hutool.core.bean.BeanUtil;

import cn.hutool.core.util.ObjectUtil;

import cn.stylefeng.guns.core.exception.BusinessException;

import cn.stylefeng.guns.modular.business.entity.Car;

import cn.stylefeng.guns.modular.business.exception.CarExceptionEnum;

import cn.stylefeng.guns.modular.business.mapper.CarMapper;

import cn.stylefeng.guns.modular.business.pojo.CarRequest;

import cn.stylefeng.guns.modular.business.service.CarService;

import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;

import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;

import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

 

import java.util.List;

 

/**

 * 车辆管理业务实现层

 *

 * @author stylefeng

 * @date 2020/3/25 14:00

 */

@Service

public class CarServiceImpl extends ServiceImpl implements CarService {

 

    @Override

    public void add(CarRequest carRequest) {

        Car car = new Car();

        BeanUtil.copyProperties(carRequest, car);

        this.save(car);

    }

 

    @Override

    @Transactional(rollbackFor = Exception.class)

    public void del(CarRequest carRequest) {

        Car car = this.queryCar(carRequest);

        this.removeById(car.getCarId());

    }

 

    @Override

    public void edit(CarRequest carRequest) {

        Car car = this.queryCar(carRequest);

        BeanUtil.copyProperties(carRequest, car);

        this.updateById(car);

    }

 

    @Override

    public Car detail(CarRequest carRequest) {

        return this.queryCar(carRequest);

    }

 

    @Override

    public PageResult findPage(CarRequest carRequest) {

        LambdaQueryWrapper wrapper = createWrapper(carRequest);

        Page sysRolePage = this.page(PageFactory.defaultPage(), wrapper);

        return PageResultFactory.createPageResult(sysRolePage);

    }

 

    @Override

    public List findList(CarRequest carRequest) {

        LambdaQueryWrapper wrapper = this.createWrapper(carRequest);

        return this.list(wrapper);

    }

 

    /**

     * 获取车辆信息

     *

     * @author stylefeng

     * @date 2020/3/25 14:00

     */

    private Car queryCar(CarRequest carRequest) {

        Car car = this.getById(carRequest.getCarId());

        if (ObjectUtil.isEmpty(car)) {

            throw new BusinessException(CarExceptionEnum.CAR_NOT_EXISTED);

        }

        return car;

    }

 

    /**

     * 创建查询wrapper

     *

     * @author fengshuonan

     * @date 2020/3/25 14:00

     */

    private LambdaQueryWrapper createWrapper(CarRequest carRequest) {

        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();

 

        String carName = carRequest.getCarName();

        queryWrapper.like(ObjectUtil.isNotEmpty(carName), Car::getCarName, carName);

 

        // 根据时间倒序排列

        queryWrapper.orderByDesc(Car::getCreateTime);

 

        return queryWrapper;

    }

 

 

}

CarService

package cn.stylefeng.guns.modular.business.service;

 

import cn.stylefeng.guns.modular.business.entity.Car;

import cn.stylefeng.guns.modular.business.pojo.CarRequest;

import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;

import com.baomidou.mybatisplus.extension.service.IService;

 

import java.util.List;

 

/**

 * 车辆管理业务层

 *

 * @author stylefeng

 * @date 2020/3/25 14:00

 */

public interface CarService extends IService {

 

    /**

     * 添加车辆

     *

     * @param carRequest 添加参数

     * @author stylefeng

     * @date 2020/3/25 14:00

     */

    void add(CarRequest carRequest);

 

    /**

     * 删除车辆

     *

     * @param carRequest 删除参数

     * @author stylefeng

     * @date 2020/3/25 14:00

     */

    void del(CarRequest carRequest);

 

    /**

     * 编辑车辆

     *

     * @param carRequest 编辑参数

     * @author stylefeng

     * @date 2020/3/25 14:00

     */

    void edit(CarRequest carRequest);

 

    /**

     * 查看车辆详情

     *

     * @param carRequest 查看参数

     * @author stylefeng

     * @date 2020/3/25 14:00

     */

    Car detail(CarRequest carRequest);

 

    /**

     * 分页查询车辆

     *

     * @param carRequest 查询参数

     * @return 查询分页结果

     * @author stylefeng

     * @date 2020/3/25 14:00

     */

    PageResult findPage(CarRequest carRequest);

 

    /**

     * 查询所有车辆

     *

     * @param carRequest 查询参数

     * @return 查询分页结果

     * @author stylefeng

     * @date 2020/3/25 14:00

     */

    List findList(CarRequest carRequest);

 

}

因此我们可以得到这样的一个前端页面

 

三、实验总结

Guns的知识复杂泷乱,不太能一时间学会,并且触类旁通,只能慢慢的一点点的跟老师给的教程做出了成果,虽然没有独立自主完成,成就感还是有的。

TRANSLATE with x English
Arabic Hebrew Polish
Bulgarian Hindi Portuguese
Catalan Hmong Daw Romanian
Chinese Simplified Hungarian Russian
Chinese Traditional Indonesian Slovak
Czech Italian Slovenian
Danish Japanese Spanish
Dutch Klingon Swedish
English Korean Thai
Estonian Latvian Turkish
Finnish Lithuanian Ukrainian
French Malay Urdu
German Maltese Vietnamese
Greek Norwegian Welsh
Haitian Creole Persian  
Bing Webmaster Portal Back