ERP跨境电商系统项目总结

在这里插入图片描述

一 根据id删除一个

前端请求

export const BrandDel =(params) =>{return axios.delete("/api/erpBrand/del?id=" + params).then(res => res.data) };

后端接受

@DeleteMapping("/del")
    public R deleteById(Long id) {
        logger.info("前端传入id{}"+id);
        return R.ok(this.erpBrandService.deleteById(id));
    }

二echart报表前端

1设置容器

<template>
  <div>
    <!-- 1设置容器 -->
    <div id="chart" style="width: 600px; height: 400px;"></div>
  </div>
</template>

2钩子函数

mounted() {
    this.initChart()
  },

3方法内使用

  methods: {
    initChart() {
      // 2初始化
      const chart = echarts.init(document.getElementById('chart'))
      // 3配置
      axios.get('/api/erpGoods/echarts').then(res => {
        console.log("res",res)
        let retData = res.data.data;

        // const options = {
        //   title: {
        //     text: '商品分类数量展示'
        //   },
        //   tooltip: {
        //     trigger: 'axis'
        //   },
        //   legend: {
        //     data: ['商品分类']
        //   },
        //   xAxis: {
        //     type: 'category',
        //     data: retData.Type    // 从后端获得x轴展示内容
        //   },
        //   yAxis: {
        //     type: 'value'
        //   },
        //   series: [
        //     {
        //       name: '商品分类',
        //       type: 'bar',
        //       data: retData.Num  // 从后端获得x轴展示数据
        //     }
        //   ]
        // }

        // 使用图表配置
        // chart.setOption(options)
          

        var data = [];

        for (var i = 0; i < retData.Num.length; i++) {
          data.push({ value: retData.Num[i], name: retData.Type[i] });
        }
      const  option = {
          title: {
            text: '商品分类数量统计',
            left: 'center'
          },
          tooltip: {
            trigger: 'item'
          },
          legend: {
            orient: 'vertical',
            left: 'left',
            itemHeight: 10,
          },
          series: [
            {
              name: 'Access From',
              type: 'pie',
              radius: '50%',
              center:['60%','50%'],
              data: data,
              emphasis: {
                itemStyle: {
                  shadowBlur: 10,
                  shadowOffsetX: 0,
                  shadowColor: 'rgba(0, 0, 0, 0.5)'
                }
              }
            }
          ]
        };
        option && chart.setOption(option);
      })
    }
  }

前端展示

柱状图’bar’

在这里插入图片描述

饼状图’pie’

在这里插入图片描述

后端代码

/**
    * 查全部数据里的分类和分类数量
    * */
    @GetMapping("/echarts")
    public R showEcharts() {
        List<HashMap<String,Object>> resultMap = erpGoodsService.queryTypeNum();
        ArrayList<String> typeList = new ArrayList<>( );
        ArrayList<Object> numList = new ArrayList<>( );
        resultMap.forEach(map -> {
            String type = (String)map.get("type");
            typeList.add(type);
            Object num=map.get("num");
            numList.add(num);
        });
        HashMap<String, Object> map = new HashMap<>( );
        map.put("Type",typeList);
        map.put("Num",numList);
        /**
         * {
         *     Type: [类型],
         *     Num: [num]
         * }
         */
        return R.ok(map);
    }

mapper.xml

<select id="queryTypeNum" resultType="java.util.HashMap">
        SELECT type,count(goods_id) num
        from erp_goods
        GROUP BY type
    </select>

mapper

List<HashMap<String,Object>> queryTypeNum();

三导入导出Excel

pom

  <!-- EasyExcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.6</version>
      </dependency>

前端按钮

<el-button size="small" type="primary" icon="el-icon-plus" @click="importExcel()">导入商品</el-button>
        <el-button size="small" type="success" icon="el-icon-upload2" @click="exportExcel()">导出商品</el-button>

绑定事件

data(){
    return{
        dialogUpVisible:false,
    }
},


importSuccess(){
      this.dialogUpVisible=false
      this.$message.success("导入成功!")
    },
importExcel(){
     this.dialogUpVisible=true
    },

前端导入操作类

<el-dialog title="导入" :visible.sync="dialogUpVisible" width="30%">
    <el-upload
      class="upload-demo"
      drag
      action="http://localhost:9999/api/erpGoods/import"
      name="file"
      method="post"
      accept=".xlsx,.xls"
      :on-success="importSuccess"
      >
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
      <div class="el-upload__tip" slot="tip">只能上传xlsx/xls文件b</div>
    </el-upload>
  </el-dialog>

后端接口

  /**
     * 导入excel实体数据
     * */
    @PostMapping("/import")
    public R importExcel(MultipartFile file) throws IOException {
        // 别忘了给SysUserReadListener构造方法传入SysUserService对象
        EasyExcel.read(file.getInputStream(),
                ErpGoods.class,
                new ErpGoodsReadListener(erpGoodsService)).sheet().doRead();
        return R.ok();
    }

实体类格式

@Data
@NoArgsConstructor
@AllArgsConstructor
@HeadRowHeight(20) // 指定列头行高
@ColumnWidth(20)
public class ErpGoods implements Serializable {
    private static final long serialVersionUID = 163297199659489178L;
    /**
     * 商品ID
     */
   @ExcelIgnore
    private Long goodsId;
    /**
     * 图片
     */
    @ExcelIgnore
    private String goodsImg;
    /**
     * 品名/SKU
     */
    @ExcelProperty(value="品名/SKU",index=0)
    private String productName;
    /**
     * 分类
     */
    @ExcelProperty(value="分类",index=1)
    private String type;
    /**
     * 品牌
     */
    @ExcelProperty(value="品牌",index=2)
    private String brand;
    /**
     * 采购人
     */
    @ExcelProperty(value="采购人",index=3)
    private String buyName;
    /**
     * 采购成本
     */
    @ExcelIgnore
    private Double purchasingCost;
    /**
     * 创建日期
     */
    @ExcelProperty(value="创建日期",index=4)
    private Date createDate;
    /**
     * 状态
     */
    @ExcelIgnore
    private String status;

导入工具类

package com.raccoon.erpsys.utils;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.raccoon.erpsys.entity.ErpGoods;
import com.raccoon.erpsys.service.ErpGoodsService;

import java.util.ArrayList;

public class ErpGoodsReadListener extends AnalysisEventListener<ErpGoods> {
    // 有个很重要的点 DemoDataListener 不能被spring管理,
    // 要每次读取excel都要new,然后里面用到spring可以构造方法传进去
    private ErpGoodsService erpGoodsService;
    public ErpGoodsReadListener(){} // 空参构造

    // 有参构造
    public ErpGoodsReadListener(ErpGoodsService erpGoodsService){
        this.erpGoodsService = erpGoodsService;
    }

    // 每隔5条存储数据库,实际项目中使用时可以100条,然后清理list ,方便内存回收
    private final int BATCH_COUNT = 5;
    private ArrayList<ErpGoods> list = new ArrayList<>( );

    // 每读一样,会调用该invoke方法一次
    @Override
    public void invoke(ErpGoods data, AnalysisContext context) {
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size( ) >= BATCH_COUNT) {
            saveData( );
            // 存储完成清理 list
            list.clear();
        }
    }

    // 全部读完之后,会调用该方法
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        if(list.size()!=0){
          saveData( );
        }
        System.out.println("所有数据解析完成!");
    }

    private void saveData() {
        System.out.println("开始存储数据库!");

        // 调用业务层存数据库
        erpGoodsService.saveBatch(list);
        System.out.println("存储数据库成功!");
    }
}

service

 int saveBatch(ArrayList<ErpGoods> list);

serviceImpl

@Override
    public int saveBatch(ArrayList<ErpGoods> list) {
        return erpGoodsMapper.saveBatch(list);
    }

mapper

 int saveBatch(ArrayList<ErpGoods> list);

mapper.xml

<insert id="saveBatch" >
        insert into erp_goods(goods_img, product_name, type, brand, buy_name, purchasing_cost, create_date, status)
        values
        <foreach collection="list" item="entity" separator=",">
            (#{entity.goodsImg}, #{entity.productName}, #{entity.type}, #{entity.brand}, #{entity.buyName}, #{entity.purchasingCost}, #{entity.createDate}, #{entity.status})
        </foreach>
</insert>

导出前端

 exportExcel(){
      this.$confirm('确定要导出数据吗?, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        axios.request({
          url:'/api/erpGoods/export',
          method:'get',
          responseType:'blob',
        }).then(res=>{
            this.download(res,"系统用户信息")
        })
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消导出'
        });
      });
    },
    download(res,name){
      console.log("调用download之后",res,name)
      if(!res){
        return;
      }
      console.log("res",res)
      let url =window.URL.createObjectURL(new Blob([res.data],
        {type:"application/vnd.ms-excel"}))
      let link=document.createElement('a');
      link.style.display='none';
      link.href=url;
      link.setAttribute('download',name);
      document.body.appendChild(link);
      link.click();
    },

后端接口


    /**
     * 导出excel实体数据
     * */
    @GetMapping("/export")
    public void exportExcel(HttpServletResponse response) throws IOException {
        // 调用业务层,查询全部数据
        List<ErpGoods> list = erpGoodsService.queryAll();
        // 这里需要设置不关闭流
        EasyExcel.write(response.getOutputStream(), ErpGoods.class).autoCloseStream(Boolean.FALSE).sheet("商品信息")
                .doWrite(list);
    }

四批量删除前端传递数组

前端请求

export const GoodsDelAll= (params) =>{return axios.post("/api/erpGoods/delAll",params)}

后端接收

    /**
     * 批量删除数据
     *
     * @param ids
     * @return 删除是否成功
     */
    @PostMapping("/delAll")
    public R delAll(@RequestBody List<Integer> ids){
        int i = erpGoodsService.delAll(ids);
        return R.ok(i);
    }

mapper.xml

<delete id="delAll">
        delete from erp_goods where goods_id in
        <foreach collection="list" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
 </delete>

五post请求以表单形式发送

前端

export const GoodsEdit = (params)  => { return req("post", "/api/erpGoods/edit", params) };

前端封装请求格式

const req = (method, url, params) => {
    return axios({
        method: method,
        url: url,
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            token: localStorage.getItem('logintoken')
        },
        data: params,
        traditional: true,
        transformRequest: [
            function(data) {
                let ret = ''
                for (let it in data) {
                    ret +=
                        encodeURIComponent(it) +
                        '=' +
                        encodeURIComponent(data[it]) +
                        '&'
                }
                return ret
            }
        ]
    }).then(res => res.data);
};

后端接受

/**
     * 编辑数据
     *
     * @param erpGoods 实体
     * @return 编辑结果
     */
    @PostMapping("/edit")
    public R edit(ErpGoods erpGoods) {
        return R.ok(this.erpGoodsService.update(erpGoods));
    }

六联查

有分类表有品牌表 当添加商品时商品下拉选择有的品牌和分类

在添加组件中用生命周期函数查询所有分类和所有品牌显示在添加下拉框内

在这里插入图片描述

前端代码

<el-form-item label="分类" prop="type">
  <!--<el-input v-model.number="ruleForm.type"></el-input>-->
  <template>
    <el-select v-model="ruleForm.type" placeholder="请选择">
      <el-option
        v-for="item in typeName"
        :key="item"
        :label="item"
        :value="item">
      </el-option>
    </el-select>
  </template>
</el-form-item>

后端接口

 /**
    * 查询全部的分类名
    * */
    @PostMapping("/queryTN")
    public R queryTN(){
        List<String> typeName = erpGoodstypeService.queryTN();

        return R.ok(typeName);
    }
  /**
     * 查询全部品牌
     * @param
     * @return list
     * */

    @PostMapping("/queryBN")
    public R queryBN(){
        List<String> brandName = erpBrandService.queryBN();
        return R.ok(brandName);
    }

mapper.xml

 <!--查询所有品牌名-->
    <select id="queryBN" resultType="java.lang.String">
        select brand_name from erp_brand
    </select>

七上传头像

pom.xml

   <!-- 使用工具类 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>

后端

 /**
     * upload file
     */
    @PostMapping("/upload")
    public R upload(MultipartFile img, HttpServletRequest request) throws IOException {
// 1.获得上传的对象 参数img就是文件对象

        // 2. 获得最终上传的目的地路径(上传至服务器中当前项目下)
        // 通过servlet方法获得路径,即最终上传到Tomcat的/upload
        String realPath = request.getServletContext().getRealPath("/upload");
        System.out.println(realPath);
        // 2.1 将最终目的文件夹创建出来
        File file = new File(realPath);
        // 判断该文件是否存在
        if(!file.exists()) {
            // 不存在则创建出
            file.mkdir();
        }
        // 2.2 获得文件名
        /*
         * 文件名重复时不能重复上传文件
         */
        String fileName = img.getOriginalFilename();
        System.out.println(fileName);
        /*
         * 根据.拆分字符串,获得文件后缀名
         */
        String[] split = fileName.split("\\.");
        System.out.println(Arrays.toString(split));
        String suffix = split[split.length-1];
        // 以当前毫秒值为文件名
        long prefix = System.currentTimeMillis();
        // 组装文件名
        String newFileName = prefix+"."+suffix;
        System.out.println("新的文件名 : "+newFileName);
        // 2.3 确定上传路径
        File newFile = new File(file,newFileName);
        // 3. 用工具上传
        FileUtils.writeByteArrayToFile(newFile, img.getBytes());
        // 4 返回路径,测试使用,放查看是否上传成功
        // String path = "http://localhost:8081/upload/"+newFileName;
        System.out.println("http://localhost:8848/upload/"+newFileName );

        HttpSession session = request.getSession( );
        SysUser sysUser = (SysUser) session.getAttribute("user");

        SysUser user = new SysUser( );
        user.setUserId(sysUser.getUserId());
        user.setAvatar("http://localhost:8848/upload/"+newFileName);
        int i = sysUserService.update(user);
        return R.ok("http://localhost:8848/upload/"+newFileName);
    }

前端

   <el-dialog title="修改头像" :visible.sync="dialogVisible" width="20%" @close="dialogVisible = false">
      <el-upload
        class="avatar-uploader"
        action="http://localhost:9999/api/sysUser/upload"
        :show-file-list="true"
        :on-success="handleAvatarSuccess"
        :before-upload="beforeAvatarUpload"
         name="img">
        <img v-if="imageUrl" :src="imageUrl" class="avatar-2">
        <i v-else class="el-icon-plus avatar-uploader-icon"></i>
      </el-upload>
    </el-dialog>

事件

  handleAvatarSuccess(res, file) {
      console.log("res",res,"文件",file)
      if(res.code===200){
       this.dialogVisible=false
        this.circleUrl=res.data+""
        console.log("对circleUrl赋值后",this.circleUrl)
      }
    },
    beforeAvatarUpload(file) {
      const isJPG = file.type === 'image/jpeg';
      const isLt2M = file.size / 1024 / 1024 < 2;

      if (!isJPG) {
        this.$message.error('上传头像图片只能是 JPG 格式!');
      }
      if (!isLt2M) {
        this.$message.error('上传头像图片大小不能超过 2MB!');
      }
      return isJPG && isLt2M;
    },

八日志log4j

pom.xml

 <dependency> <!-- 引入log4j2依赖 -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
 </dependency>
 <!-- 去掉springboot默认配置 -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
             </exclusions>

在resourcse建一个log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!-- status log4j2内部输出自身的日志信息的级别,可以不设置,没太大用 -->
<!-- configuration中主要包括有 PropertiesAppendersLoggers标签 -->
<Configuration status="fatal" monitorInterval="30">
    <!--打印在本地,根据具体存储地址填写 ./logs是当前项目名下的位置-->
    <Properties>
        <Property name="baseDir" value="./logs"/>
        <!--
			常见的配置如下:
        - %d{yyyy-MM-dd HH:mm:ss.SSS} : 日志生成时间,输出格式为“年--日 时::.毫秒”
        - %p : 日志输出格式
        - %c : logger的名称
        - %m : 日志内容,即 logger.info("message")
        - %n : 换行符
        - %T : 线程号
        - %L : 日志输出所在行数
        - %M : 日志输出所在方法名
        -->
        <Property name="pattern">%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %c{1}:%L -%m%n</Property>
    </Properties>
    <!-- 输出源,常见的主要有ConsoleRollingFileFile 三种子节点
        Console:用于定义输出到控制台的Appender
        RollingFile:定义指定方式触发新的Appender
      -->
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout>
                <pattern>${pattern}</pattern>
            </PatternLayout>
        </Console>

        <!--debug级别日志文件输出-->
        <!--<RollingFile name="debug_appender" fileName="${baseDir}/debug.log"-->
        <!--             filePattern="${baseDir}/debug_%i.log.%d{yyyy-MM-dd}">-->
        <!--    &lt;!&ndash; 过滤器 &ndash;&gt;-->
        <!--    <Filters>-->
        <!--        &lt;!&ndash; 限制日志级别在debug及以上在info以下 &ndash;&gt;-->
        <!--        <ThresholdFilter level="debug"/>-->
        <!--        <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>-->
        <!--    </Filters>-->
        <!--    &lt;!&ndash; 日志格式 &ndash;&gt;-->
        <!--    <PatternLayout>-->
        <!--        <pattern>${pattern}</pattern>-->
        <!--    </PatternLayout>-->
        <!--    &lt;!&ndash; 策略 &ndash;&gt;-->
        <!--    <Policies>-->
        <!--        &lt;!&ndash; 每隔一天转存 &ndash;&gt;-->
        <!--        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>-->
        <!--        &lt;!&ndash; 文件大小 &ndash;&gt;-->
        <!--        <SizeBasedTriggeringPolicy size="100 MB"/>-->
        <!--    </Policies>-->
        <!--</RollingFile>-->

        <!-- info级别日志文件输出 -->
        <!--<RollingFile name="info_appender" fileName="${baseDir}/info.log"-->
        <!--             filePattern="${baseDir}/info_%i.log.%d{yyyy-MM-dd}">-->
        <!--    &lt;!&ndash; 过滤器 &ndash;&gt;-->
        <!--    <Filters>-->
        <!--        &lt;!&ndash; 限制日志级别在info及以上在error以下 &ndash;&gt;-->
        <!--        <ThresholdFilter level="info"/>-->
        <!--        <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>-->
        <!--    </Filters>-->
        <!--    &lt;!&ndash; 日志格式 &ndash;&gt;-->
        <!--    <PatternLayout>-->
        <!--        <pattern>${pattern}</pattern>-->
        <!--    </PatternLayout>-->
        <!--    &lt;!&ndash; 策略 &ndash;&gt;-->
        <!--    <Policies>-->
        <!--        &lt;!&ndash; 每隔一天转存 &ndash;&gt;-->
        <!--        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>-->
        <!--        &lt;!&ndash; 文件大小 &ndash;&gt;-->
        <!--        <SizeBasedTriggeringPolicy size="100 MB"/>-->
        <!--    </Policies>-->
        <!--</RollingFile>-->

        <!-- error级别日志文件输出 -->
        <RollingFile name="error_appender" fileName="${baseDir}/error.log"
                     filePattern="${baseDir}/error_%i.log.%d{yyyy-MM-dd}">
            <!-- 过滤器 -->
            <Filters>
                <!-- 限制日志级别在error及以上 -->
                <ThresholdFilter level="error"/>
            </Filters>
            <!-- 日志格式 -->
            <PatternLayout>
                <pattern>${pattern}</pattern>
            </PatternLayout>
            <Policies>
                <!-- 每隔一天转存 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <!-- 文件大小 -->
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <!-- 这是总的级别配置,优先级比Appenders中的要高 -->
        <Root level="DEBUG">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="debug_appender"/>
            <AppenderRef ref="info_appender"/>
            <AppenderRef ref="error_appender"/>
        </Root>
    </Loggers>
</Configuration>

使用

 private Logger logger = LogManager.getLogger(SysUserController.class);

九AOP实战-log记录

pom.xml

    <!--Aop-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

日期sql

CREATE TABLE `log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `log_time` datetime DEFAULT NULL,
  `log` varchar(255) DEFAULT NULL,
  `ip` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

日志注解文件

package com.qf.annotation;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    String value();
}

使用注解

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 先登录,登录成功后将信息存入Session
     * 这样log日志记录时才能取出人名
     */
    @GetMapping("/login")
    public User login(String name, HttpSession session) {
        User user = userService.selectUserByUsername(name);

        if (user != null) {
            session.setAttribute("user",user);
        }
        return user;
    }


    @GetMapping("/list")
    @MyLog("查询全部") // 使用日志注解
    public List<User> selectAll() {
        return userService.selectAll();
    }
}

切面类

package com.qf.aspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import com.qf.annotation.MyLog;
import com.qf.model.Log;
import com.qf.model.User;
import com.qf.service.MyLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.WebUtils;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
@Component
@Aspect
public class MyLogAspect {
    // 注入业务层对象
    @Autowired
    private MyLogService logService;

    // 切入的目标是注解
    @Before("@annotation(com.qf.annotation.MyLog)")
    public void after(JoinPoint joinPoint) {
        // 调用下方自定义方法,获得注解中的值
        String desc = getAnnoDesc(joinPoint);

        System.out.println("注解中的值:" + desc);
        // 以下代码需要使用Springmvc,既控制层也需要由Spring托管,才会获得ip
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes( )).getRequest( );
        // 能取出数据的前提是登录时将用户信息存储到session
        User user = (User) WebUtils.getSessionAttribute(request, "user");

        String name = "未登录";
        if (user != null) { // 防止出现空指针异常
            name = user.getName( );
        }

        //ip
        String ip = request.getRemoteAddr( );
        System.out.println("请求对象的ip:" + ip);
        // 封装数据
        Log log = new Log( );
        log.setName(name);
        log.setIp(ip);
        log.setLogTime(new Date());
        log.setLog(desc);
        // 调用业务层,执行插入
        logService.insertLog(log);
    }

    /*
     * 封装的方法,通过反射技术获得目标方法上的注解的值
     */
    public static String getAnnoDesc(JoinPoint joinPoint){
        String value = "";
        // 获得目标方法名
        String methodName = joinPoint.getSignature( ).getName( );
        System.out.println("---------->" + methodName );// 目标方法名
        // 根据目标方法所在的对象得到该类的全部方法
        Method[] methods = joinPoint.getTarget().getClass().getDeclaredMethods();
        /*
         *  1.遍历所有方法
         *  2.找到与目标方法一样的方法
         *  3.找到该方法上的所有注解
         *  4.遍历所有注解,判断注解的类型是否是我们自定义的日志注解
         *  5.如果是,就获取该注解对象
         *  6.由注解对象得到注解值
         */

        // 1.遍历所有方法
        for (Method method : methods) {
            // 2.找到与目标方法一样的方法
            if (methodName.equals(method.getName())) {
                // 3.找到该方法上的所有注解
                Annotation[] annotations = method.getDeclaredAnnotations();
                // 4.遍历所有注解,判断注解的类型是否是我们自定义的日志注解
                for (Annotation annotation : annotations) {
                    // 5.如果是,就获取该注解对象
                    if (annotation.annotationType( ).equals(com.qf.util.Log.class)) {
                        // 6.由注解对象得到注解值
                        value = method.getAnnotation(com.qf.util.Log.class).value( );
                        return value;
                    }
                }
            }
        }
        return value;
    }
}

十两表联查

前端

  <el-form-item label="品牌" prop="brand">
    <!--<el-input v-model.number="ruleForm.brand"></el-input>-->
    <template>
        <el-select v-model="ruleForm.brand" placeholder="请选择">
          <el-option
            v-for="item in BrandName"
            :key="item"
            :label="item"
            :value="item">
          </el-option>
        </el-select>
    </template>
  </el-form-item>

事件和绑定变量

data(){
return{
  BrandName:[],
}
}
method:{
   getTypeName(){
      TypeQueryTN().then(res=>{
        if(res.code===2000){
          console.log("BrandQueryBN",res)
          this.typeName=res.data
        }
      })
    },
}

后端

    /**
     * 查询全部品牌
     * @param
     * @return list
     * */

    @PostMapping("/queryBN")
    public R queryBN(){
        List<String> brandName = erpBrandService.queryBN();
        return R.ok(brandName);
    }

mapper.xml

   <!--查询所有品牌名-->
    <select id="queryBN" resultType="java.lang.String">
        select brand_name from erp_brand
    </select>

十一ElemenntUI组件

面包屑

 <!-- 面包屑导航 -->
  <el-breadcrumb separator-class="el-icon-arrow-right">
    <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
    <el-breadcrumb-item>品牌管理</el-breadcrumb-item>
  </el-breadcrumb>

下拉选择框

 <template>
        <el-select v-model="ruleForm.brand" placeholder="请选择">
          <el-option
            v-for="item in BrandName"
            :key="item"
            :label="item"
            :value="item">
          </el-option>
        </el-select>
    </template>

头部菜单

  <el-menu class="el-menu-demo" mode="horizontal" background-color="#334157" text-color="#fff" active-text-color="#fff">
    <el-button class="buttonimg">
      <img class="showimg" :src="collapsed?imgsq:imgshow" @click="toggle(collapsed)">
    </el-button>
    <el-submenu index="2" class="submenu">
       <template slot="title">{{erpuser.userName}}</template>
      <!--<template slot="title">超级管理员</template>-->
      <!--<el-menu-item @click="content1()" index="2-1">修改密码</el-menu-item>-->
      <el-menu-item @click="content()" index="2-2">个人中心</el-menu-item>
      <el-menu-item @click="exit()" index="2-3">退出</el-menu-item>
    </el-submenu>
  </el-menu>

十二添加用户异步校验是否存在

前端

 userName: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          {validator: validateUserName, trigger: 'blur'}
        ],
   var validateUserName = (rule, value, callback) => {
     console.log(value)
      axios.post('api/sysUser/count',{
        userName:value
      }).then(res=>{
        console.log("返回数据",res.data)
        if(res.data.data>0){
          callback(new Error('用户名已存在'))
        }else{
          callback()
        }
      })
    }
``
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Shope跨境电商ERP源码是一种用于跨境电商企业的管理系统的源代码。这种源码提供了一套完整的解决方案,用于帮助企业管理和监控其在跨境电商领域的业务。 首先,Shope跨境电商ERP源码具备了强大的仓储管理功能。它可以帮助企业实时追踪和管理其海外仓库的库存情况,包括收货、上架、销售出库等环节。通过这个功能,企业可以更好地掌握物流情况,做到库存的精细化管理,提高运营效率。 其次,该源码还提供了订单管理功能。企业可以通过系统来管理和处理客户的订单,包括订单的创建、支付、物流追踪等环节。这个功能可以帮助企业实现订单的快速处理,提高客户满意度。 此外,Shope跨境电商ERP源码还具备财务管理功能。企业可以通过系统来管理和记录其财务数据,包括收入、支出、利润等。通过这个功能,企业可以更好地进行财务分析和决策,确保企业的财务状况良好。 最后,该源码还提供了报表和分析功能。企业可以根据系统提供的各种报表和分析结果,对企业运营情况进行评估和优化。通过这个功能,企业可以更好地了解自身的市场表现和潜在机会,从而制定更加精细化的经营策略。 总之,Shope跨境电商ERP源码是一款功能强大的管理系统,能够帮助企业实现跨境电商业务的高效运营和管理。 ### 回答2: Shope跨境电商ERP源码是一种用于构建和管理跨境电商平台的软件代码。它提供了丰富的功能和工具,帮助企业实现跨境业务的高效运营和管理。以下是关于Shope跨境电商ERP源码的一些重要信息。 首先,Shope跨境电商ERP源码具有强大的订单管理功能。它能够实时追踪和处理订单,方便客户和企业之间的沟通和合作。通过Shope ERP源码,用户可以轻松管理和处理来自不同国家和地区的订单,包括订单审核、库存管理和物流跟踪等。 其次,Shope ERP源码提供了全面的产品管理工具。用户可以通过该源码管理产品信息、价格和库存等。这些功能可以帮助企业更加灵活地调整和控制产品的上架和销售,以适应不同市场和需求。 此外,Shope跨境电商ERP源码还支持跨境支付和结算。它与不同的支付接口集成,可以方便地处理跨境交易的支付和结算事务。这为企业提供了便捷和安全的支付方式,保障了跨境交易的顺利推进和资金安全。 最后,Shope ERP源码还包含了一些额外的功能,例如数据分析和报告生成。这些功能可以帮助企业分析和评估销售数据,了解市场趋势和消费者行为,从而做出更加明智的商业决策。 总体而言,Shope跨境电商ERP源码是一种功能强大且灵活的软件代码,适用于各类跨境电商企业。它提供了订单管理、产品管理、支付结算和数据分析等多项核心功能,帮助企业实现高效运营和管理,促进跨境业务的发展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值