商城后端

master
miaoqingshuai 2026-01-16 17:13:15 +08:00
parent 12fb035b2e
commit 4dfa2301df
46 changed files with 2280 additions and 149 deletions

View File

@ -34,6 +34,11 @@ public class SysConfigController extends BaseController
@Autowired
private ISysConfigService configService;
@GetMapping("/test")
public String list(){
return "test";
}
/**
*
*/

View File

@ -47,6 +47,9 @@ public class SysLoginController
@Autowired
private ISysConfigService configService;
/**
*
*
@ -58,7 +61,7 @@ public class SysLoginController
{
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(),
loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
return ajax;

View File

@ -6,9 +6,9 @@ spring:
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: password
url: jdbc:mysql://193.112.94.36:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ry-vue
password: yKSYaNzYifKFNHHj
# 从库数据源
slave:
# 从数据源开关/默认关闭

View File

@ -7,7 +7,7 @@ ruoyi:
# 版权年份
copyrightYear: 2026
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
profile: D:/ruoyi/uploadPath
profile: /home/ruoyi/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
@ -68,13 +68,13 @@ spring:
# redis 配置
redis:
# 地址
host: localhost
host: 193.112.94.36
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password:
password: 123456
# 连接超时时间
timeout: 10s
lettuce:

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="/home/ruoyi/logs" />
<property name="log.path" value="./logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />

View File

@ -113,6 +113,13 @@
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!--新加Lombok包 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
</project>

View File

@ -15,6 +15,7 @@ import com.ruoyi.common.core.domain.BaseEntity;
*
* @author ruoyi
*/
public class SysDept extends BaseEntity
{
private static final long serialVersionUID = 1L;

View File

@ -60,10 +60,10 @@ public class SysLoginService
* @param uuid
* @return
*/
public String login(String username, String password, String code, String uuid)
public String login(String username, String password, String uuid)
{
// 验证码校验
validateCaptcha(username, code, uuid);
// validateCaptcha(username, code, uuid);
// 登录前置校验
loginPreCheck(username, password);
// 用户验证

View File

@ -189,7 +189,7 @@ public class VelocityUtils
}
else if (template.contains("mapper.java.vm"))
{
fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
fileName = StringUtils.format("{}/mapper/{}mapper.java", javaPath, className);
}
else if (template.contains("service.java.vm"))
{
@ -205,7 +205,7 @@ public class VelocityUtils
}
else if (template.contains("mapper.xml.vm"))
{
fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className);
fileName = StringUtils.format("{}/{}mapper.xml", mybatisPath, className);
}
else if (template.contains("sql.vm"))
{

97
ruoyi-mall/pom.xml Normal file
View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.9.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>ruoyi-mall</artifactId>
<description>
微店商城服务
</description>
<dependencies>
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
<!-- swagger3-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
<!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-framework</artifactId>
</dependency>
<!-- 定时任务-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-quartz</artifactId>
</dependency>
<!-- 代码生成-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-generator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.15</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>${project.artifactId}</warName>
</configuration>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
</project>

View File

@ -0,0 +1,20 @@
package com.ruoyi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
*
*
* @author ruoyi
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MallApplication
{
public static void main(String[] args)
{
SpringApplication.run(MallApplication.class, args);
System.out.println("商城Mall启动成功");
}
}

View File

@ -0,0 +1,18 @@
package com.ruoyi;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* web
*
* @author ruoyi
*/
public class RuoYiServletInitializer extends SpringBootServletInitializer
{
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
return application.sources(MallApplication.class);
}
}

View File

@ -0,0 +1,66 @@
package com.ruoyi.web.controller;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.web.domain.Brand;
import com.ruoyi.web.service.BrandService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/mall/brand")
public class BrandController extends BaseController {
@Autowired
private BrandService brandService;
/**
*
*
* @return
*/
@Anonymous
@GetMapping("/tree")
public AjaxResult getBrandTree() {
return AjaxResult.success(brandService.getBrandTree());
}
/**
*
*
* @param brand
* @return
*/
@Anonymous
@Log(title = "商品品牌管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
public AjaxResult addBrand(@RequestBody Brand brand) {
brand.setCreateBy(getUsername());
return toAjax(brandService.addBrand(brand));
}
/**
*
*
* @param brand
* @return
*/
@Anonymous
@Log(title = "商品品牌管理", businessType = BusinessType.UPDATE)
@PostMapping("/update")
public AjaxResult updateBrand(@RequestBody Brand brand) {
return toAjax(brandService.updateBrand(brand));
}
// 删除品牌
@DeleteMapping("/delete/{id}")
public String deleteBrand(@PathVariable Long id) {
brandService.deleteBrand(id);
return "success";
}
}

View File

@ -0,0 +1,148 @@
package com.ruoyi.web.controller;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.web.domain.Product;
import com.ruoyi.web.domain.Store;
import com.ruoyi.web.dto.BatchStoreDTO;
import com.ruoyi.web.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
/**
*
*/
@RestController
@RequestMapping("/mall/product")
public class ProductStoreController extends BaseController {
@Autowired
private ProductService productService;
/**
*
*/
@PreAuthorize("@ss.hasPermi('product:list')")
@GetMapping("/list")
public AjaxResult list(Product product) {
return AjaxResult.success(productService.selectList(product));
}
/**
*
*/
@Anonymous
@DeleteMapping("/batchDelete")
public AjaxResult batchDelete(@RequestBody BatchStoreDTO dto) {
return toAjax(productService.batchProductById(dto));
}
/**
* id
*/
@PreAuthorize("@ss.hasPermi('product:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable Long id) {
return success(productService.selectProductBarCode(id));
}
/**
*
*/
@PreAuthorize("@ss.hasPermi('product:add')")
@Log(title = "商品管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
public AjaxResult add(@Validated Product product,
@RequestParam("file") MultipartFile file) throws IOException, InvalidExtensionException {
if (!productService.checkProductCodeUnique(product)) {
return error("新增商品条码'" + product.getProductBarCode() + "'失败,商品条码已存在");
}
// TODO: 2026/1/14 这个file 为空的时候 就使用国码里面的图片,不为空代表用户自己选择了图片上传 用自己服务器上的
//国码后续添加
if (!file.isEmpty()) {
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
if (StringUtils.isNotEmpty(avatar)) {
product.setMainImage(avatar);
}
}
product.setCreateBy(getUsername());
return toAjax(productService.insertProductStore(product));
}
/**
*
*/
// @PreAuthorize("@ss.hasPermi('product:edit')")
@Anonymous
@Log(title = "商品管理", businessType = BusinessType.UPDATE)
@PostMapping("/update")
public AjaxResult edit(@Validated Product product,
@RequestParam("file") MultipartFile file) throws IOException, InvalidExtensionException {
if (!productService.checkProductCodeUnique(product)) {
return error("修改商品条码'" + product.getProductBarCode() + "'失败,商品条码已存在");
}
// TODO: 2026/1/14 这个file 为空的时候 就使用国码里面的图片,不为空代表用户自己选择了图片上传 用自己服务器上的
//然后修改的时候 这个file不为空代表用户又修改了图片需要更新为空的话之前图片保持不变
if (!file.isEmpty()) {
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
if (StringUtils.isNotEmpty(avatar)) {
product.setMainImage(avatar);
}
}
//todo 这里可以国码有的时候使用国码的图片路径
product.setUpdateBy(getUsername());
return toAjax(productService.updateProduct(product));
}
/**
*
*/
@PreAuthorize("@ss.hasPermi('product:delete')")
@Log(title = "商品管理", businessType = BusinessType.DELETE)
@DeleteMapping("/delete/{id}")
public AjaxResult remove(@PathVariable Long id) {
return toAjax(productService.deleteProductById(id));
}
/**
* excel
*/
@Log(title = "商品管理", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('product:import')")
@PostMapping("/importData")
public AjaxResult importData(@RequestParam("file") MultipartFile file,
@RequestParam("storeId") Integer storeId) throws Exception {
ExcelUtil<Product> util = new ExcelUtil<Product>(Product.class);
List<Product> productList = util.importExcel(file.getInputStream());
return AjaxResult.success(productService.importProduct(productList,storeId));
}
}

View File

@ -0,0 +1,96 @@
package com.ruoyi.web.controller;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.web.domain.Store;
import com.ruoyi.web.dto.UserStoreAssignDTO;
import com.ruoyi.web.service.StoreService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
*
*/
@RestController
@RequestMapping("/mall/store")
public class StoreController extends BaseController {
@Autowired
private StoreService storeService;
/**
*
*/
@PreAuthorize("@ss.hasPermi('store:list')")
@GetMapping("/list")
public TableDataInfo list(Store store) {
startPage();
List<Store> list = storeService.selectList(store);
return getDataTable(list);
}
/**
*
*/
@PreAuthorize("@ss.hasPermi('store:add')")
@Log(title = "门店管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
public AjaxResult add(@Validated @RequestBody Store store) {
if (!storeService.checkStoreNameUnique(store)) {
return error("新增门店'" + store.getStoreCode() + "'失败,门店编码已存在");
}
store.setCreateBy(getUsername());
return toAjax(storeService.insertStore(store));
}
/**
*
*/
@PreAuthorize("@ss.hasPermi('store:delete')")
@Log(title = "门店管理", businessType = BusinessType.DELETE)
@DeleteMapping("/delete/{storeId}")
public AjaxResult remove(@PathVariable Long storeId) {
return toAjax(storeService.deleteStoreById(storeId));
}
/**
*
*/
@PreAuthorize("@ss.hasPermi('store:edit')")
@Log(title = "门店管理", businessType = BusinessType.UPDATE)
@PutMapping("update")
public AjaxResult edit(@Validated @RequestBody Store store) {
if (!storeService.checkStoreNameUnique(store)) {
return error("修改门店编码'" + store.getStoreCode() + "'失败,门店编码已存在");
}
store.setUpdateBy(getUsername());
return toAjax(storeService.updateStore(store));
}
/**
*
*/
@PreAuthorize("@ss.hasPermi('user:store:edit')")
@Log(title = "用户关联门店", businessType = BusinessType.GRANT)
@PutMapping("/userCorrelationStore")
public AjaxResult userCorrelationStore(@RequestBody UserStoreAssignDTO dto) {
return toAjax(storeService.insertUserStores(dto.getUserId(), dto.getStoreIds()));
}
/**
* id
*/
// @PreAuthorize("@ss.hasPermi('product:query')")
@GetMapping(value = "/getUserStore/{userId}")
public AjaxResult getUserStore(@PathVariable String userId) {
return success(storeService.getUserStore(userId));
}
}

View File

@ -0,0 +1,125 @@
package com.ruoyi.web.core.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
/**
* Swagger2
*
* @author ruoyi
*/
@Configuration
public class SwaggerConfig
{
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
/** 是否开启swagger */
@Value("${swagger.enabled}")
private boolean enabled;
/** 设置请求的统一前缀 */
@Value("${swagger.pathMapping}")
private String pathMapping;
/**
* API
*/
@Bean
public Docket createRestApi()
{
return new Docket(DocumentationType.OAS_30)
// 是否启用Swagger
.enable(enabled)
// 用来创建该API的基本信息展示在文档的页面中自定义展示的信息
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有有注解的api用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解
// .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
/* 设置安全模式swagger可以设置访问token */
.securitySchemes(securitySchemes())
.securityContexts(securityContexts())
.pathMapping(pathMapping);
}
/**
* tokenAuthorization
*/
private List<SecurityScheme> securitySchemes()
{
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
return apiKeyList;
}
/**
*
*/
private List<SecurityContext> securityContexts()
{
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
.build());
return securityContexts;
}
/**
*
*/
private List<SecurityReference> defaultAuth()
{
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
}
/**
*
*/
private ApiInfo apiInfo()
{
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题若依管理系统_接口文档")
// 描述
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
// 版本
.version("版本号:" + ruoyiConfig.getVersion())
.build();
}
}

View File

@ -0,0 +1,20 @@
package com.ruoyi.web.domain;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import java.util.List;
/**
* mall_brand
*/
@Data
public class Brand extends BaseEntity {
private static final long serialVersionUID = 1L;
private Long id;
private Long parentId = 0L; // 0表示一级品牌
private String brandName; // 品牌名称
private Integer storeId; //门店id
// 非数据库字段:子商品品牌列表
private List<Brand> children;
}

View File

@ -0,0 +1,108 @@
package com.ruoyi.web.domain;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* mall_product_store
*/
@Data
public class Product extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
*
*/
@Excel(name = "商品编码")
private String productCode;
/**
*
*/
@Excel(name = "货架码")
private String shelfCode;
/**
*
*/
private Integer shelfLife;
/**
*
*/
private String productionDate;
/**
*
*/
private Integer approaching;
/**
*
*/
@NotBlank(message = "商品条码不能为空")
@Excel(name = "商品条码")
private String productBarCode;
/**
*
*/
@NotBlank(message = "商品名称不能为空")
@Excel(name = "商品名称")
private String productName;
/**
*
*/
@Excel(name = "商品描述")
private String productDesc;
/**
*
*/
private String mainImage;
/**
*
*/
private BigDecimal storePrice;
/**
*
*/
private BigDecimal costPrice;
/**
*
*/
private Long originalPrice;
/**
* ID
*/
private Integer storeId;
/**
*
*/
@NotNull(message = "库存数量不能为空")
@Excel(name = "库存数量")
private Integer stockQuantity;
/**
*
*/
private Integer soldQuantity;
/**
* 0 1
*/
private String status;
}

View File

@ -0,0 +1,59 @@
package com.ruoyi.web.domain;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
/**
* mall_store
*/
@Data
public class Store extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long storeId;
/**
*
*/
private String storeName;
/**
*
*/
private String storeCode;
/**
*
*/
private String province;
/**
*
*/
private String city;
/**
*
*/
private String district;
/**
*
*/
private String fullAddress;
/**
*
*/
private String contactPhone;
/**
*
*/
private String storeManager;
}

View File

@ -0,0 +1,11 @@
package com.ruoyi.web.dto;
import lombok.Data;
import java.util.List;
@Data
public class BatchStoreDTO {
private List<Long> ids;
}

View File

@ -0,0 +1,9 @@
package com.ruoyi.web.dto;
import lombok.Data;
@Data
public class UserStoreAssignDTO {
private Long userId;
private Long[] storeIds;
}

View File

@ -0,0 +1,56 @@
package com.ruoyi.web.mapper;
import com.ruoyi.web.domain.Brand;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface BrandMapper {
/**
*
*/
List<Brand> selectAll();
/**
* parentId=0
*/
List<Brand> selectRootBrands();
/**
* ID
*/
List<Brand> selectByParentId(@Param("parentId") Long parentId);
/**
* ID
*/
Brand selectById(@Param("id") Long id);
/**
*
*/
int insert(Brand brand);
/**
*
*/
int update(Brand brand);
/**
*
*/
int delete(@Param("id") Long id);
// /**
// * 检查是否有子品牌
// */
// int countChildren(@Param("parentId") Long parentId);
//
// /**
// * 根据品牌名称查询
// */
// Brand selectByName(@Param("brandName") String brandName);
}

View File

@ -0,0 +1,29 @@
package com.ruoyi.web.mapper;
import com.ruoyi.web.domain.Product;
import com.ruoyi.web.domain.Store;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
*
*/
public interface ProductMapper {
List<Product> selectList(Product product);
Product checkProductCodeUnique(Product product);
int insertProductStore(Product product);
Product selectProductBarCode(Long id);
int deleteProductById(Long id);
int updateProduct(Product product);
int updateProductBarCode(Product product);
int batchProductById(@Param("ids") List<Long> ids);
}

View File

@ -0,0 +1,32 @@
package com.ruoyi.web.mapper;
import com.ruoyi.web.domain.Store;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
*
*
*/
public interface StoreMapper
{
List<Store> selectList(Store store);
int insertStore(Store store);
Store checkStoreNameUnique(@Param("storeCode") String storeCode,@Param("storeId") Long storeId);
int deleteStoreById(Long storeId);
int updateStore(Store store);
void deleteUserStore(Long userId);
int insertUserStore(@Param("userId") Long userId, @Param("storeIds") Long[] storeIds);
void deleteStoreUser(Long storeId);
}

View File

@ -0,0 +1,12 @@
package com.ruoyi.web.mapper;
import com.ruoyi.web.vo.UserStoreVo;
/**
*
*/
public interface UserStoreMapper {
UserStoreVo getUserStoreWithStores(String userId);
}

View File

@ -0,0 +1,20 @@
package com.ruoyi.web.service;
import com.ruoyi.web.domain.Brand;
import java.util.List;
public interface BrandService {
// 获取品牌树(一级品牌包含子品牌)
List<Brand> getBrandTree();
// 添加品牌
int addBrand(Brand brand);
int updateBrand(Brand brand);
// 删除品牌
void deleteBrand(Long id);
}

View File

@ -0,0 +1,38 @@
package com.ruoyi.web.service;
import com.ruoyi.web.domain.Product;
import com.ruoyi.web.dto.BatchStoreDTO;
import java.util.List;
/**
*
*
*/
public interface ProductService {
/**
*
* @param product
* @return
*/
List<Product> selectList(Product product);
/**
*
* @param product
* @return
*/
boolean checkProductCodeUnique(Product product);
int insertProductStore(Product product);
int importProduct(List<Product> productList,Integer storeId);
Product selectProductBarCode(Long id);
int deleteProductById(Long id);
int updateProduct(Product product);
int batchProductById(BatchStoreDTO dto);
}

View File

@ -0,0 +1,59 @@
package com.ruoyi.web.service;
import com.ruoyi.web.domain.Store;
import com.ruoyi.web.vo.UserStoreVo;
import java.util.List;
/**
*
*
* @author ruoyi
*/
public interface StoreService {
/**
*
* @param store
* @return
*/
List<Store> selectList(Store store);
/**
*
* @param store
* @return
*/
int insertStore(Store store);
/**
*
* @param store
* @return
*/
boolean checkStoreNameUnique(Store store);
/**
*
* @param storeId
* @return
*/
int deleteStoreById(Long storeId);
/**
*
* @param store
* @return
*/
int updateStore(Store store);
/**
*
* @param userId
* @param storeIds
* @return
*/
int insertUserStores(Long userId, Long[] storeIds);
UserStoreVo getUserStore(String userId);
}

View File

@ -0,0 +1,57 @@
package com.ruoyi.web.service.impl;
import com.ruoyi.web.domain.Brand;
import com.ruoyi.web.mapper.BrandMapper;
import com.ruoyi.web.service.BrandService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class BrandServiceImpl implements BrandService {
@Autowired
private BrandMapper brandMapper;
@Override
public List<Brand> getBrandTree() {
// 1. 获取所有一级品牌
List<Brand> rootBrands = brandMapper.selectRootBrands();
// 2. 为每个一级品牌设置子品牌
for (Brand rootBrand : rootBrands) {
List<Brand> children = brandMapper.selectByParentId(rootBrand.getId());
rootBrand.setChildren(children);
}
return rootBrands;
}
@Override
@Transactional
public int addBrand(Brand brand) {
// 如果是二级品牌,检查父品牌是否存在
if (brand.getParentId() != null && brand.getParentId() != 0) {
Brand parent = brandMapper.selectById(brand.getParentId());
if (parent == null || parent.getParentId() != 0) {
throw new RuntimeException("父品牌不存在或不是一级品牌");
}
} else {
brand.setParentId(0L); // 确保一级品牌parentId=0
}
return brandMapper.insert(brand);
}
@Override
@Transactional
public int updateBrand(Brand brand) {
return brandMapper.update(brand);
}
@Override
public void deleteBrand(Long id) {
}
}

View File

@ -0,0 +1,82 @@
package com.ruoyi.web.service.impl;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.domain.Product;
import com.ruoyi.web.dto.BatchStoreDTO;
import com.ruoyi.web.mapper.ProductMapper;
import com.ruoyi.web.service.ProductService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
*
*/
@Service
public class ProductServiceImpl implements ProductService {
@Resource
private ProductMapper productMapper;
@Override
public List<Product> selectList(Product product) {
return productMapper.selectList(product);
}
@Override
public boolean checkProductCodeUnique(Product product) {
if (product == null || StringUtils.isBlank(product.getProductBarCode())) {
return UserConstants.UNIQUE;
}
Long id = product.getId() == null ? -1L : product.getId();
product.setId(id);
Product product1 = productMapper.checkProductCodeUnique(product);
return product1 != null ? UserConstants.NOT_UNIQUE : UserConstants.UNIQUE;
}
@Override
public int insertProductStore(Product product) {
return productMapper.insertProductStore(product);
}
@Override
public int importProduct(List<Product> productList, Integer storeId) {
for (Product product : productList) {
product.setStoreId(storeId);
//判断同条形码是否存在,区分不同门店
if (!checkProductCodeUnique(product)) {
productMapper.updateProductBarCode(product);
} else {
productMapper.insertProductStore(product);
}
}
return 1;
}
@Override
public Product selectProductBarCode(Long id) {
return productMapper.selectProductBarCode(id);
}
@Override
public int deleteProductById(Long id) {
return productMapper.deleteProductById(id);
}
@Override
public int updateProduct(Product product) {
return productMapper.updateProduct(product);
}
@Override
public int batchProductById(BatchStoreDTO dto) {
if (dto.getIds() == null || dto.getIds().isEmpty()) {
return 0;
}
return productMapper.batchProductById(dto.getIds());
}
}

View File

@ -0,0 +1,89 @@
package com.ruoyi.web.service.impl;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.domain.Store;
import com.ruoyi.web.mapper.StoreMapper;
import com.ruoyi.web.mapper.UserStoreMapper;
import com.ruoyi.web.service.StoreService;
import com.ruoyi.web.vo.UserStoreVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
/**
*
*
* @author ruoyi
*/
@Service
public class StoreServiceImpl implements StoreService {
@Resource
private StoreMapper storeMapper;
@Resource
private UserStoreMapper userStoreMapper;
@Override
public List<Store> selectList(Store store) {
return storeMapper.selectList(store);
}
@Override
public int insertStore(Store store) {
return storeMapper.insertStore(store);
}
@Override
public boolean checkStoreNameUnique(Store store) {
if (store == null || StringUtils.isBlank(store.getStoreCode())) {
return UserConstants.UNIQUE;
}
// 获取当前门店ID新增时为-1
Long storeId = store.getStoreId() == null ? -1L : store.getStoreId();
// 查询除自身外是否有相同编码的门店
Store existStore = storeMapper.checkStoreNameUnique(
store.getStoreCode(),
storeId
);
// 如果查询到记录,说明编码已被其他门店使用
return existStore != null ? UserConstants.NOT_UNIQUE : UserConstants.UNIQUE;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteStoreById(Long storeId) {
storeMapper.deleteStoreUser(storeId);
return storeMapper.deleteStoreById(storeId);
}
@Override
public int updateStore(Store store) {
return storeMapper.updateStore(store);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int insertUserStores(Long userId, Long[] storeIds) {
//先把直接这个用户关联的门店删除掉
storeMapper.deleteUserStore(userId);
//再新增新的用户关联门店
if (storeIds != null && storeIds.length > 0) {
return storeMapper.insertUserStore(userId, storeIds);
}
return 1;
}
@Override
public UserStoreVo getUserStore(String userId) {
return userStoreMapper.getUserStoreWithStores(userId);
}
}

View File

@ -0,0 +1,22 @@
package com.ruoyi.web.vo;
import lombok.Data;
@Data
public class StoreVo {
/**
* ID
*/
private Long storeId;
/**
*
*/
private String storeName;
/**
*
*/
private String storeCode;
}

View File

@ -0,0 +1,12 @@
package com.ruoyi.web.vo;
import lombok.Data;
import java.util.List;
@Data
public class UserStoreVo {
private Long userId; //用户id
private List<StoreVo> stores; // 门店对象列表
}

View File

@ -0,0 +1 @@
restart.include.json=/com.alibaba.fastjson2.*.jar

View File

@ -0,0 +1,61 @@
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://193.112.94.36:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ry-vue
password: yKSYaNzYifKFNHHj
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: ruoyi
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true

View File

@ -0,0 +1,136 @@
# 项目相关配置
ruoyi:
# 名称
name: RuoYi
# 版本
version: 3.9.1
# 版权年份
copyrightYear: 2026
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
profile: /home/ruoyi/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8081
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数默认为100
accept-count: 1000
threads:
# tomcat最大线程数默认为200
max: 800
# Tomcat启动初始化的线程数默认值10
min-spare: 100
# 日志配置
logging:
level:
com.ruoyi: debug
org.springframework: warn
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间默认10分钟
lockTime: 10
# Spring配置
spring:
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: druid
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
# redis 配置
redis:
# 地址
host: 193.112.94.36
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password: 123456
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期默认30分钟
expireTime: 30
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
# Swagger配置
swagger:
# 是否开启swagger
enabled: true
# 请求前缀
pathMapping: /dev-api
# 防盗链配置
referer:
# 防盗链开关
enabled: false
# 允许的域名列表
allowed-domains: localhost,127.0.0.1,ruoyi.vip,www.ruoyi.vip
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*

View File

@ -0,0 +1,24 @@
Application Version: ${ruoyi.version}
Spring Boot Version: ${spring-boot.version}
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,38 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="./logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.ruoyi" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>

View File

@ -0,0 +1,77 @@
<?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="com.ruoyi.web.mapper.BrandMapper">
<resultMap id="BaseResultMap" type="com.ruoyi.web.domain.Brand">
<id property="id" column="id"/>
<result column="parent_id" property="parentId" />
<result column="brand_name" property="brandName" />
<result column="create_time" property="createTime" />
<result column="update_time" property="updateTime" />
<result column="create_by" property="createBy" />
<result column="update_by" property="updateBy" />
<result column="store_id" property="storeId" />
</resultMap>
<!-- 查询所有品牌 -->
<select id="selectAll" resultMap="BaseResultMap">
SELECT *
FROM brand
ORDER BY parent_id, sort_order
</select>
<select id="selectRootBrands" resultMap="BaseResultMap">
SELECT *
FROM mall_brand
WHERE parent_id = 0 and del_flag = '0'
</select>
<!-- 根据父ID查询子品牌 -->
<select id="selectByParentId" resultMap="BaseResultMap">
SELECT *
FROM mall_brand
WHERE parent_id = #{parentId}
</select>
<!-- 根据ID查询品牌 -->
<select id="selectById" resultMap="BaseResultMap" parameterType="long">
SELECT id, parent_id, brand_name
FROM mall_brand
WHERE id = #{id}
</select>
<insert id="insert" parameterType="Brand">
insert into mall_brand (
<if test="parentId != null">parent_id,</if>
<if test="brandName != null and brandName != ''">brand_name,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="storeId != null">store_id,</if>
create_time
)values(
<if test="parentId != null">#{parentId},</if>
<if test="brandName != null and brandName != ''">#{brandName},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="storeId != null">#{storeId},</if>
sysdate()
)
</insert>
<update id="update">
UPDATE mall_brand
SET brand_name = #{brandName}
WHERE id = #{id}
</update>
<!-- 删除品牌 -->
<delete id="delete">
DELETE
FROM brand
WHERE id = #{id}
</delete>
</mapper>

View File

@ -0,0 +1,215 @@
<?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="com.ruoyi.web.mapper.ProductMapper">
<resultMap type="Product" id="MallProductResult">
<id property="id" column="id"/>
<result property="productCode" column="product_code"/>
<result property="productName" column="product_name"/>
<result property="productDesc" column="product_desc"/>
<result property="mainImage" column="main_image"/>
<result property="storePrice" column="store_price"/>
<result property="costPrice" column="cost_price"/>
<result property="storeId" column="store_id"/>
<result property="stockQuantity" column="stock_quantity"/>
<result property="soldQuantity" column="sold_quantity"/>
<result property="status" column="status"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="productBarCode" column="product_bar_code"/>
<result property="shelfCode" column="shelf_code"/>
<result property="shelfLife" column="shelf_life"/>
<result property="approaching" column="approaching"/>
<result property="productionDate" column="production_date"/>
</resultMap>
<sql id="selectCommodityVo">
select id,
product_code,
product_name,
product_desc,
main_image,
store_price,
cost_price,
store_id,
stock_quantity,
sold_quantity,
status,
create_by,
create_time,
update_by,
update_time,
remark,
product_bar_code,
shelf_code,
shelf_life,
production_date,
approaching
from mall_product_store
</sql>
<select id="selectList" parameterType="Product" resultMap="MallProductResult">
<include refid="selectCommodityVo"/>
<where>
del_flag = '0'
<if test="productBarCode != null and productBarCode != ''">
AND product_bar_code = #{productBarCode}
</if>
<if test="productName != null and productName != ''">
AND product_name like concat('%', #{productName}, '%')
</if>
<if test="storeId != null">
AND store_id = #{storeId}
</if>
<!-- 库存大于0条件 -->
AND stock_quantity > 0
<if test="status != null and status != '' ">
AND status = #{status}
</if>
</where>
</select>
<select id="checkProductCodeUnique" resultType="com.ruoyi.web.domain.Product">
<include refid="selectCommodityVo"/>
where product_bar_code=#{productBarCode} and del_flag = '0' and store_id = #{storeId}
<if test="id != null">
AND id != #{id}
</if>
limit 1
</select>
<select id="selectProductBarCode" parameterType="Product" resultMap="MallProductResult">
<include refid="selectCommodityVo"/>
where id=#{id} and del_flag = '0'
</select>
<insert id="insertProductStore" parameterType="Product">
insert into mall_product_store (
<if test="productCode != null and productCode != ''">product_code,</if>
<if test="productName != null and productName != ''">product_name,</if>
<if test="productDesc != null and productDesc != ''">product_desc,</if>
<if test="mainImage != null and mainImage != ''">main_image,</if>
<if test="storePrice != null and storePrice != ''">store_price,</if>
<if test="costPrice != null and costPrice != ''">cost_price,</if>
<if test="originalPrice != null and originalPrice != ''">original_price,</if>
<if test="storeId != null and storeId != ''">store_id,</if>
<if test="stockQuantity != null and stockQuantity != ''">stock_quantity,</if>
<if test="soldQuantity != null and soldQuantity != ''">sold_quantity,</if>
<if test="status != null and status != ''">status,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="remark != null and remark != ''">remark,</if>
<if test="shelfCode != null and shelfCode != ''">shelf_code,</if>
<if test="productBarCode != null and productBarCode != ''">product_bar_code,</if>
<if test="shelfLife != null and shelfLife != ''">shelf_life,</if>
<if test="productionDate != null and productionDate != ''">production_date,</if>
<if test="approaching != null">approaching,</if>
create_time
)values(
<if test="productCode != null and productCode != ''">#{productCode},</if>
<if test="productName != null and productName != ''">#{productName},</if>
<if test="productDesc != null and productDesc != ''">#{productDesc},</if>
<if test="mainImage != null and mainImage != ''">#{mainImage},</if>
<if test="storePrice != null and storePrice != ''">#{storePrice},</if>
<if test="costPrice != null and costPrice != ''">#{costPrice},</if>
<if test="originalPrice != null and originalPrice != ''">#{originalPrice},</if>
<if test="storeId != null and storeId != ''">#{storeId},</if>
<if test="stockQuantity != null and stockQuantity != ''">#{stockQuantity},</if>
<if test="soldQuantity != null and soldQuantity != ''">#{soldQuantity},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="remark != null and remark != ''">#{remark},</if>
<if test="shelfCode != null and shelfCode != ''">#{shelfCode},</if>
<if test="productBarCode != null and productBarCode != ''">#{productBarCode},</if>
<if test="shelfLife != null and shelfLife != ''">#{shelfLife},</if>
<if test="productionDate != null and productionDate != ''">#{productionDate},</if>
<if test="approaching != null">#{approaching},</if>
sysdate()
)
</insert>
<update id="updateProduct">
update mall_product_store
<set>
<if test="productCode != null and productCode != ''">product_code = #{productCode},</if>
<if test="productName != null and productName != ''">product_name = #{productName},</if>
<if test="productDesc != null and productDesc != ''">product_desc = #{productDesc},</if>
<if test="mainImage != null and mainImage != ''">main_image = #{mainImage},</if>
<if test="storePrice != null and storePrice != ''">store_price = #{storePrice},</if>
<if test="costPrice != null and costPrice != ''">cost_price = #{costPrice},</if>
<if test="stockQuantity != null and stockQuantity != ''">stock_quantity = #{stockQuantity},</if>
<if test="soldQuantity != null and soldQuantity != ''">sold_quantity = #{soldQuantity},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="shelfCode != null and shelfCode != ''">shelf_code = #{shelfCode},</if>
<if test="productBarCode != null and productBarCode != ''">product_bar_code = #{productBarCode},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="shelfLife != null">shelf_life = #{shelfLife},</if>
<if test="productionDate != null and productionDate != ''">production_date = #{productionDate},</if>
<if test="approaching != null">approaching = #{approaching},</if>
update_time = sysdate()
</set>
where id = #{id}
</update>
<update id="updateProductBarCode">
update mall_product_store
<set>
<if test="productCode != null and productCode != ''">product_code = #{productCode},</if>
<if test="productName != null and productName != ''">product_name = #{productName},</if>
<if test="productDesc != null and productDesc != ''">product_desc = #{productDesc},</if>
<if test="mainImage != null and mainImage != ''">main_image = #{mainImage},</if>
<if test="storePrice != null and storePrice != ''">store_price = #{storePrice},</if>
<if test="costPrice != null and costPrice != ''">cost_price = #{costPrice},</if>
<if test="stockQuantity != null and stockQuantity != ''">stock_quantity = #{stockQuantity},</if>
<if test="soldQuantity != null and soldQuantity != ''">sold_quantity = #{soldQuantity},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="shelfCode != null and shelfCode != ''">shelf_code = #{shelfCode},</if>
<if test="productBarCode != null and productBarCode != ''">product_bar_code = #{productBarCode},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="shelfLife != null">shelf_life = #{shelfLife},</if>
<if test="productionDate != null and productionDate != ''">production_date = #{productionDate},</if>
<if test="approaching != null">approaching = #{approaching},</if>
update_time = sysdate()
</set>
where product_bar_code = #{productBarCode}
</update>
<delete id="deleteConfigById" parameterType="Long">
delete
from sys_config
where config_id = #{configId}
</delete>
<delete id="deleteConfigByIds" parameterType="Long">
delete from sys_config where config_id in
<foreach item="configId" collection="array" open="(" separator="," close=")">
#{configId}
</foreach>
</delete>
<delete id="deleteProductById">
update mall_product_store
set del_flag = '2'
where id = #{id}
</delete>
<delete id="batchProductById">
UPDATE mall_product_store
SET del_flag = 2
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -0,0 +1,138 @@
<?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="com.ruoyi.web.mapper.StoreMapper">
<resultMap type="Store" id="MallStoreResult">
<id property="storeId" column="store_id"/>
<result property="storeName" column="store_name"/>
<result property="storeCode" column="store_code"/>
<result property="province" column="province"/>
<result property="city" column="city"/>
<result property="district" column="district"/>
<result property="fullAddress" column="full_address"/>
<result property="contactPhone" column="contact_phone"/>
<result property="storeManager" column="store_manager"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<sql id="selectStoreVo">
select store_id,
store_name,
store_code,
province,
city,
district,
full_address,
contact_phone,
store_manager,
create_by,
create_time,
update_by,
update_time,
remark
from mall_store
</sql>
<select id="selectList" parameterType="Store" resultMap="MallStoreResult">
<include refid="selectStoreVo"/>
<where>
del_flag = '0'
<if test="storeName != null and storeName != ''">
AND store_name = #{storeName}
</if>
</where>
</select>
<select id="checkStoreNameUnique" resultType="com.ruoyi.web.domain.Store">
<include refid="selectStoreVo"/>
where store_code=#{storeCode} and del_flag = '0'
<if test="storeId != null">
AND store_id != #{storeId}
</if>
limit 1
</select>
<insert id="insertStore" parameterType="Store">
insert into mall_store (
<if test="storeName != null and storeName != '' ">store_name,</if>
<if test="storeCode != null and storeCode != '' ">store_code,</if>
<if test="province != null and province != '' ">province,</if>
<if test="city != null and city != '' ">city,</if>
<if test="district != null and city != '' ">district,</if>
<if test="fullAddress != null and fullAddress != '' ">full_address,</if>
<if test="contactPhone != null and contactPhone != '' ">contact_phone,</if>
<if test="storeManager != null and storeManager != '' ">store_manager,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="remark != null and remark != ''">remark,</if>
create_time
)values(
<if test="storeName != null and storeName != ''">#{storeName},</if>
<if test="storeCode != null and storeCode != ''">#{storeCode},</if>
<if test="province != null and province != ''">#{province},</if>
<if test="city != null and city != ''">#{city},</if>
<if test="district != null and district != ''">#{district},</if>
<if test="fullAddress != null and fullAddress != ''">#{fullAddress},</if>
<if test="contactPhone != null and contactPhone != ''">#{contactPhone},</if>
<if test="storeManager != null and storeManager != ''">#{storeManager},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="remark != null and remark != ''">#{remark},</if>
sysdate()
)
</insert>
<insert id="insertUserStore">
INSERT INTO mall_user_store(user_id, store_id)
VALUES
<foreach collection="storeIds" item="storeId" separator=",">
(#{userId}, #{storeId})
</foreach>
</insert>
<update id="updateStore">
update mall_store
<set>
<if test="storeName != null and storeName != ''">store_name = #{storeName},</if>
<if test="storeCode != null and storeCode != ''">store_code = #{storeCode},</if>
<if test="province != null and province != ''">province = #{province},</if>
<if test="city != null and city != ''">city = #{city},</if>
<if test="district != null and district != ''">district = #{district},</if>
<if test="fullAddress != null and fullAddress != ''">full_address = #{fullAddress},</if>
<if test="contactPhone != null and contactPhone != ''">contact_phone = #{contactPhone},</if>
<if test="storeManager != null and storeManager != ''">store_manager = #{storeManager},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="remark != null">remark = #{remark},</if>
update_time = sysdate()
</set>
where store_id = #{storeId}
</update>
<delete id="deleteStoreById">
update mall_store
set del_flag = '2'
where store_id = #{storeId}
</delete>
<delete id="deleteUserStore">
delete
from mall_user_store
where user_id = #{userId}
</delete>
<delete id="deleteStoreUser">
delete
from mall_user_store
where store_id = #{storeId}
</delete>
</mapper>

View File

@ -0,0 +1,27 @@
<?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="com.ruoyi.web.mapper.UserStoreMapper">
<resultMap id="userStoreWithStoresMap" type="com.ruoyi.web.vo.UserStoreVo">
<id property="userId" column="user_id"/>
<collection property="stores" ofType="com.ruoyi.web.vo.StoreVo">
<result property="storeName" column="store_name"/>
<result property="storeId" column="store_id"/>
<result property="storeCode" column="store_code"/>
</collection>
</resultMap>
<select id="getUserStoreWithStores" resultMap="userStoreWithStoresMap">
SELECT mus.user_id,
s.store_id,
s.store_name,
s.store_code
FROM mall_user_store mus
LEFT JOIN mall_store s ON mus.store_id = s.store_id
WHERE mus.user_id = #{userId}
</select>
</mapper>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 全局参数 -->
<settings>
<!-- 使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 允许JDBC 支持自动生成主键 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
</settings>
</configuration>

View File

@ -14,7 +14,7 @@
#app {
height: 100%;
margin: 0px;
padding: 0px;
padding: 0px;
}
.chromeframe {
margin: 0.2em 0;

View File

@ -1,136 +1,136 @@
'use strict'
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
const CompressionPlugin = require('compression-webpack-plugin')
const name = process.env.VUE_APP_TITLE || '若依管理系统' // 网页标题
const baseUrl = 'http://localhost:8080' // 后端接口
const port = process.env.port || process.env.npm_config_port || 80 // 端口
// vue.config.js 配置说明
//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
// 这里只列一部分,具体配置参考文档
module.exports = {
// 部署生产环境和开发环境下的URL。
// 默认情况下Vue CLI 会假设你的应用是被部署在一个域名的根路径上
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
// 在npm run build 或 yarn build 时 生成文件的目录名称要和baseUrl的生产环境路径一致默认dist
outputDir: 'dist',
// 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
assetsDir: 'static',
// 如果你不需要生产环境的 source map可以将其设置为 false 以加速生产环境构建。
productionSourceMap: false,
transpileDependencies: ['quill'],
// webpack-dev-server 相关配置
devServer: {
host: '0.0.0.0',
port: port,
open: true,
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
target: baseUrl,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
},
// springdoc proxy
'^/v3/api-docs/(.*)': {
target: baseUrl,
changeOrigin: true
}
},
disableHostCheck: true
},
css: {
loaderOptions: {
sass: {
sassOptions: { outputStyle: "expanded" }
}
}
},
configureWebpack: {
name: name,
resolve: {
alias: {
'@': resolve('src')
}
},
plugins: [
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
new CompressionPlugin({
cache: false, // 不启用文件缓存
test: /\.(js|css|html|jpe?g|png|gif|svg)?$/i, // 压缩文件格式
filename: '[path][base].gz[query]', // 压缩后的文件名
algorithm: 'gzip', // 使用gzip压缩
minRatio: 0.8, // 压缩比例,小于 80% 的文件不会被压缩
deleteOriginalAssets: false // 压缩后删除原文件
})
],
},
chainWebpack(config) {
config.plugins.delete('preload') // TODO: need test
config.plugins.delete('prefetch') // TODO: need test
// set svg-sprite-loader
config.module
.rule('svg')
.exclude.add(resolve('src/assets/icons'))
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/assets/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
config.when(process.env.NODE_ENV !== 'development', config => {
config
.plugin('ScriptExtHtmlWebpackPlugin')
.after('html')
.use('script-ext-html-webpack-plugin', [{
// `runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/
}])
.end()
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
},
elementUI: {
name: 'chunk-elementUI', // split elementUI into a single package
test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
priority: 20 // the weight needs to be larger than libs and app or it will be packaged into libs or app
},
commons: {
name: 'chunk-commons',
test: resolve('src/components'), // can customize your rules
minChunks: 3, // minimum common number
priority: 5,
reuseExistingChunk: true
}
}
})
config.optimization.runtimeChunk('single')
})
}
}
'use strict'
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
const CompressionPlugin = require('compression-webpack-plugin')
const name = process.env.VUE_APP_TITLE || '若依管理系统' // 网页标题
const baseUrl = 'http://193.112.94.36:8080' // 后端接口
const port = process.env.port || process.env.npm_config_port || 80 // 端口
// vue.config.js 配置说明
//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
// 这里只列一部分,具体配置参考文档
module.exports = {
// 部署生产环境和开发环境下的URL。
// 默认情况下Vue CLI 会假设你的应用是被部署在一个域名的根路径上
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
// 在npm run build 或 yarn build 时 生成文件的目录名称要和baseUrl的生产环境路径一致默认dist
outputDir: 'dist',
// 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
assetsDir: 'static',
// 如果你不需要生产环境的 source map可以将其设置为 false 以加速生产环境构建。
productionSourceMap: false,
transpileDependencies: ['quill'],
// webpack-dev-server 相关配置
devServer: {
host: '0.0.0.0',
port: 8087,
open: true,
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
target: baseUrl,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
},
// springdoc proxy
'^/v3/api-docs/(.*)': {
target: baseUrl,
changeOrigin: true
}
},
disableHostCheck: true
},
css: {
loaderOptions: {
sass: {
sassOptions: { outputStyle: "expanded" }
}
}
},
configureWebpack: {
name: name,
resolve: {
alias: {
'@': resolve('src')
}
},
plugins: [
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
new CompressionPlugin({
cache: false, // 不启用文件缓存
test: /\.(js|css|html|jpe?g|png|gif|svg)?$/i, // 压缩文件格式
filename: '[path][base].gz[query]', // 压缩后的文件名
algorithm: 'gzip', // 使用gzip压缩
minRatio: 0.8, // 压缩比例,小于 80% 的文件不会被压缩
deleteOriginalAssets: false // 压缩后删除原文件
})
],
},
chainWebpack(config) {
config.plugins.delete('preload') // TODO: need test
config.plugins.delete('prefetch') // TODO: need test
// set svg-sprite-loader
config.module
.rule('svg')
.exclude.add(resolve('src/assets/icons'))
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/assets/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
config.when(process.env.NODE_ENV !== 'development', config => {
config
.plugin('ScriptExtHtmlWebpackPlugin')
.after('html')
.use('script-ext-html-webpack-plugin', [{
// `runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/
}])
.end()
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
},
elementUI: {
name: 'chunk-elementUI', // split elementUI into a single package
test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
priority: 20 // the weight needs to be larger than libs and app or it will be packaged into libs or app
},
commons: {
name: 'chunk-commons',
test: resolve('src/components'), // can customize your rules
minChunks: 3, // minimum common number
priority: 5,
reuseExistingChunk: true
}
}
})
config.optimization.runtimeChunk('single')
})
}
}