如果汉语背后没有文化,文化背后没有思想,思想背后没有精神,光TMD编造老娘和乔布斯没有说过的话,那中国永远不会是一个伟大的国家。——撒切尔夫人

刘一辰的软件工程随笔

软件构造实验作业

班级:信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" ?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="cn.stylefeng.guns.modular.business.mapper.CarMapper">

 

</mapper>

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<Car> {

}

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<CarMapper, Car> 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<Car> findPage(CarRequest carRequest) {

        LambdaQueryWrapper<Car> wrapper = createWrapper(carRequest);

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

        return PageResultFactory.createPageResult(sysRolePage);

    }

 

    @Override

    public List<Car> findList(CarRequest carRequest) {

        LambdaQueryWrapper<Car> 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<Car> createWrapper(CarRequest carRequest) {

        LambdaQueryWrapper<Car> 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<Car> {

 

    /**

     * 添加车辆

     *

     * @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<Car> findPage(CarRequest carRequest);

 

    /**

     * 查询所有车辆

     *

     * @param carRequest 查询参数

     * @return 查询分页结果

     * @author stylefeng

     * @date 2020/3/25 14:00

     */

    List<Car> findList(CarRequest carRequest);

 

}

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

 

三、实验总结

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

posted @   崤函隳  阅读(223)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2020-12-10 2020/12/10 刘一辰的JAVA随笔
点击右上角即可分享
微信分享提示