Commit 2777caee by 杨浩

客户订单

parent 7964cbac
......@@ -119,6 +119,11 @@
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-spring-boot-starter-biz-customer-permission</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-spring-boot-starter-biz-ip</artifactId>
<version>${revision}</version>
</dependency>
......
package cn.iocoder.foodnexus.module.system.enums;
package cn.iocoder.foodnexus.framework.common.enums;
import cn.iocoder.foodnexus.framework.common.core.ArrayValuable;
import cn.iocoder.foodnexus.framework.common.enums.CommonStatusEnum;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
......
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>foodnexus-framework</artifactId>
<groupId>cn.iocoder.boot</groupId>
<version>${revision}</version>
</parent>
<artifactId>foodnexus-spring-boot-starter-biz-customer-permission</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>客户-商品-数据权限</description>
<dependencies>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-common</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-spring-boot-starter-security</artifactId>
<optional>true</optional> <!-- 可选,如果使用 DeptDataPermissionRule 必须提供 -->
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-module-product-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-module-erp-api</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</project>
package cn.iocoder.foodnexus.module.customerpermission.config;
import cn.iocoder.foodnexus.module.customerpermission.core.aop.CustomerPermissionAnnotationAdvisor;
import cn.iocoder.foodnexus.module.customerpermission.core.db.CustomerPermissionRuleHandler;
import cn.iocoder.foodnexus.module.customerpermission.core.rule.CustomerPermissionRule;
import cn.iocoder.foodnexus.module.customerpermission.core.rule.CustomerPermissionRuleFactory;
import cn.iocoder.foodnexus.module.customerpermission.core.rule.CustomerPermissionRuleFactoryImpl;
import cn.iocoder.foodnexus.framework.mybatis.core.util.MyBatisUtils;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import java.util.List;
/**
* 数据权限的自动配置类
*
* @author 芋道源码
*/
@AutoConfiguration
public class FoodnexusCustomerPermissionAutoConfiguration {
@Bean
public CustomerPermissionRuleFactory customerPermissionRuleFactory(List<CustomerPermissionRule> rules) {
return new CustomerPermissionRuleFactoryImpl(rules);
}
@Bean
public CustomerPermissionRuleHandler customerPermissionRuleHandler(MybatisPlusInterceptor interceptor,
CustomerPermissionRuleFactory ruleFactory) {
// 创建 DataPermissionInterceptor 拦截器
CustomerPermissionRuleHandler handler = new CustomerPermissionRuleHandler(ruleFactory);
DataPermissionInterceptor inner = new DataPermissionInterceptor(handler);
// 添加到 interceptor 中
// 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定
MyBatisUtils.addInterceptor(interceptor, inner, 0);
return handler;
}
@Bean
public CustomerPermissionAnnotationAdvisor customerPermissionAnnotationAdvisor() {
return new CustomerPermissionAnnotationAdvisor();
}
}
package cn.iocoder.foodnexus.module.customerpermission.core.annotation;
import java.lang.annotation.*;
/**
* @author : yanghao
* create at: 2025/9/4 10:09
* @description: 客户 - 可见数据
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomerVisible {
boolean enable() default true;
}
package cn.iocoder.foodnexus.module.customerpermission.core.aop;
import cn.iocoder.foodnexus.module.customerpermission.core.annotation.CustomerVisible;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
/**
* {@link CustomerVisible} 注解的 Advisor 实现类
*
* @author 芋道源码
*/
@Getter
@EqualsAndHashCode(callSuper = true)
public class CustomerPermissionAnnotationAdvisor extends AbstractPointcutAdvisor {
private final Advice advice;
private final Pointcut pointcut;
public CustomerPermissionAnnotationAdvisor() {
this.advice = new CustomerPermissionAnnotationInterceptor();
this.pointcut = this.buildPointcut();
}
protected Pointcut buildPointcut() {
Pointcut classPointcut = new AnnotationMatchingPointcut(CustomerVisible.class, true);
Pointcut methodPointcut = new AnnotationMatchingPointcut(null, CustomerVisible.class, true);
return new ComposablePointcut(classPointcut).union(methodPointcut);
}
}
package cn.iocoder.foodnexus.module.customerpermission.core.aop;
import cn.iocoder.foodnexus.module.customerpermission.core.annotation.CustomerVisible;
import lombok.Getter;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.MethodClassKey;
import org.springframework.core.annotation.AnnotationUtils;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* {@link CustomerVisible} 注解的拦截器
* 1. 在执行方法前,将 @CustomerVisible 注解入栈
* 2. 在执行方法后,将 @CustomerVisible 注解出栈
*
* @author 芋道源码
*/
@CustomerVisible // 该注解,用于 {@link CUSTOMER_PERMISSION_NULL} 的空对象
public class CustomerPermissionAnnotationInterceptor implements MethodInterceptor {
/**
* CustomerVisible 空对象,用于方法无 {@link CustomerVisible} 注解时,使用 CUSTOMER_PERMISSION_NULL 进行占位
*/
static final CustomerVisible CUSTOMER_PERMISSION_NULL = CustomerPermissionAnnotationInterceptor.class.getAnnotation(CustomerVisible.class);
@Getter
private final Map<MethodClassKey, CustomerVisible> CustomerVisibleCache = new ConcurrentHashMap<>();
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// 入栈
CustomerVisible CustomerVisible = this.findAnnotation(methodInvocation);
if (CustomerVisible != null) {
CustomerPermissionContextHolder.add(CustomerVisible);
}
try {
// 执行逻辑
return methodInvocation.proceed();
} finally {
// 出栈
if (CustomerVisible != null) {
CustomerPermissionContextHolder.remove();
}
}
}
private CustomerVisible findAnnotation(MethodInvocation methodInvocation) {
// 1. 从缓存中获取
Method method = methodInvocation.getMethod();
Object targetObject = methodInvocation.getThis();
Class<?> clazz = targetObject != null ? targetObject.getClass() : method.getDeclaringClass();
MethodClassKey methodClassKey = new MethodClassKey(method, clazz);
CustomerVisible CustomerVisible = CustomerVisibleCache.get(methodClassKey);
if (CustomerVisible != null) {
return CustomerVisible != CUSTOMER_PERMISSION_NULL ? CustomerVisible : null;
}
// 2.1 从方法中获取
CustomerVisible = AnnotationUtils.findAnnotation(method, CustomerVisible.class);
// 2.2 从类上获取
if (CustomerVisible == null) {
CustomerVisible = AnnotationUtils.findAnnotation(clazz, CustomerVisible.class);
}
// 2.3 添加到缓存中
CustomerVisibleCache.put(methodClassKey, CustomerVisible != null ? CustomerVisible : CUSTOMER_PERMISSION_NULL);
return CustomerVisible;
}
}
package cn.iocoder.foodnexus.module.customerpermission.core.aop;
import cn.iocoder.foodnexus.module.customerpermission.core.annotation.CustomerVisible;
import com.alibaba.ttl.TransmittableThreadLocal;
import java.util.LinkedList;
import java.util.List;
/**
* {@link CustomerVisible} 注解的 Context 上下文
*
* @author 芋道源码
*/
public class CustomerPermissionContextHolder {
/**
* 使用 List 的原因,可能存在方法的嵌套调用
*/
private static final ThreadLocal<LinkedList<CustomerVisible>> DATA_PERMISSIONS =
TransmittableThreadLocal.withInitial(LinkedList::new);
/**
* 获得当前的 CustomerVisible 注解
*
* @return CustomerVisible 注解
*/
public static CustomerVisible get() {
return DATA_PERMISSIONS.get().peekLast();
}
/**
* 入栈 CustomerVisible 注解
*
* @param CustomerVisible CustomerVisible 注解
*/
public static void add(CustomerVisible CustomerVisible) {
DATA_PERMISSIONS.get().addLast(CustomerVisible);
}
/**
* 出栈 CustomerVisible 注解
*
* @return CustomerVisible 注解
*/
public static CustomerVisible remove() {
CustomerVisible CustomerVisible = DATA_PERMISSIONS.get().removeLast();
// 无元素时,清空 ThreadLocal
if (DATA_PERMISSIONS.get().isEmpty()) {
DATA_PERMISSIONS.remove();
}
return CustomerVisible;
}
/**
* 获得所有 CustomerVisible
*
* @return CustomerVisible 队列
*/
public static List<CustomerVisible> getAll() {
return DATA_PERMISSIONS.get();
}
/**
* 清空上下文
*
* 目前仅仅用于单测
*/
public static void clear() {
DATA_PERMISSIONS.remove();
}
}
package cn.iocoder.foodnexus.module.customerpermission.core.db;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.foodnexus.module.customerpermission.core.rule.CustomerPermissionRule;
import cn.iocoder.foodnexus.module.customerpermission.core.rule.CustomerPermissionRuleFactory;
import cn.iocoder.foodnexus.framework.mybatis.core.util.MyBatisUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
import lombok.RequiredArgsConstructor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.schema.Table;
import org.springframework.stereotype.Component;
import java.util.List;
import static cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils.skipPermissionCheck;
/**
* 基于 {@link CustomerPermissionRule} 的数据权限处理器
*
* 它的底层,是基于 MyBatis Plus 的 <a href="https://baomidou.com/plugins/data-permission/">数据权限插件</a>
* 核心原理:它会在 SQL 执行前拦截 SQL 语句,并根据用户权限动态添加权限相关的 SQL 片段。这样,只有用户有权限访问的数据才会被查询出来
*
* @author 芋道源码
*/
@RequiredArgsConstructor
@Component
public class CustomerPermissionRuleHandler implements MultiDataPermissionHandler {
private final CustomerPermissionRuleFactory ruleFactory;
public static final String TABLE_NAME = "product_spu";
@Override
public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
// 特殊:跨租户访问
if (skipPermissionCheck()) {
return null;
}
// 获得 Mapper 对应的数据权限的规则
List<CustomerPermissionRule> rules = ruleFactory.getCustomerPermissionRule(mappedStatementId);
if (CollUtil.isEmpty(rules)) {
return null;
}
// 生成条件
Expression allExpression = null;
for (CustomerPermissionRule rule : rules) {
// 判断表名是否匹配
String tableName = MyBatisUtils.getTableName(table);
if (!TABLE_NAME.equalsIgnoreCase(tableName)) {
continue;
}
// 单条规则的条件
Expression oneExpress = rule.getExpression(tableName, table.getAlias());
if (oneExpress == null) {
continue;
}
// 拼接到 allExpression 中
allExpression = allExpression == null ? oneExpress
: new AndExpression(allExpression, oneExpress);
}
return allExpression;
}
}
package cn.iocoder.foodnexus.module.customerpermission.core.rule;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
/**
* @author : yanghao
* create at: 2025/9/4 11:10
* @description:
*/
public interface CustomerPermissionRule {
/**
* 根据表名和别名,生成对应的 WHERE / OR 过滤条件
*
* @param tableName 表名
* @param tableAlias 别名,可能为空
* @return 过滤条件 Expression 表达式
*/
Expression getExpression(String tableName, Alias tableAlias);
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.customerpermission.core.rule;
import java.util.List;
/**
* {@link CustomerPermissionRule} 工厂接口
* 作为 {@link CustomerPermissionRule} 的容器,提供管理能力
*
* @author 芋道源码
*/
public interface CustomerPermissionRuleFactory {
/**
* 获得所有数据权限规则数组
*
* @return 数据权限规则数组
*/
List<CustomerPermissionRule> getCustomerPermissionRules();
/**
* 获得指定 Mapper 的数据权限规则数组
*
* @param mappedStatementId 指定 Mapper 的编号
* @return 数据权限规则数组
*/
List<CustomerPermissionRule> getCustomerPermissionRule(String mappedStatementId);
}
package cn.iocoder.foodnexus.module.customerpermission.core.rule;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.foodnexus.module.customerpermission.core.annotation.CustomerVisible;
import cn.iocoder.foodnexus.module.customerpermission.core.aop.CustomerPermissionContextHolder;
import lombok.extern.slf4j.Slf4j;
import java.util.Collections;
import java.util.List;
/**
* 默认的 DataPermissionRuleFactoryImpl 实现类
* 支持通过 {@link CustomerPermissionContextHolder} 过滤数据权限
*
* @author 芋道源码
*/
@Slf4j
public class CustomerPermissionRuleFactoryImpl implements CustomerPermissionRuleFactory {
/**
* 数据权限规则数组
*/
private final List<CustomerPermissionRule> rules;
public CustomerPermissionRuleFactoryImpl(List<CustomerPermissionRule> rules) {
this.rules = rules;
}
@Override
public List<CustomerPermissionRule> getCustomerPermissionRules() {
return rules;
}
@Override // mappedStatementId 参数,暂时没有用。以后,可以基于 mappedStatementId + CustomerPermission 进行缓存
public List<CustomerPermissionRule> getCustomerPermissionRule(String mappedStatementId) {
// 1. 无数据权限
if (CollUtil.isEmpty(rules)) {
return Collections.emptyList();
}
// 2. 未配置,则默认关闭
CustomerVisible customerVisible = CustomerPermissionContextHolder.get();
if (customerVisible == null) {
return Collections.emptyList();
}
// 3. 已配置,但禁用
if (!customerVisible.enable()) {
return Collections.emptyList();
}
// 6. 已配置,全部规则
return rules;
}
}
package cn.iocoder.foodnexus.module.customerpermission.core.rule.customer;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils;
import cn.iocoder.foodnexus.framework.common.util.json.JsonUtils;
import cn.iocoder.foodnexus.module.customerpermission.core.rule.CustomerPermissionRule;
import cn.iocoder.foodnexus.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.foodnexus.framework.security.core.LoginUser;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.module.erp.api.service.ErpCustomerApi;
import cn.iocoder.foodnexus.module.product.api.InquireCustomerApi;
import cn.iocoder.foodnexus.module.product.api.dto.CustomerVisibleProductRespDTO;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Set;
/**
* @author : yanghao
* create at: 2025/9/4 11:14
* @description:
*/
@Slf4j
@Component
public class CustomerVisiblePermissionRule implements CustomerPermissionRule {
/**
* LoginUser 的 Context 缓存 Key
*/
protected static final String CONTEXT_KEY = CustomerVisiblePermissionRule.class.getSimpleName();
@Autowired
private ErpCustomerApi customerApi;
@Autowired
private InquireCustomerApi inquireCustomerApi;
public static final String PRODUCT_ID_COLUMN = "id";
/**
* 根据表名和别名,生成对应的 WHERE / OR 过滤条件
*
* @param tableName 表名
* @param tableAlias 别名,可能为空
* @return 过滤条件 Expression 表达式
*/
@Override
public Expression getExpression(String tableName, Alias tableAlias) {
// 只有有登陆用户的情况下,才进行数据权限的处理
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
if (loginUser == null) {
return null;
}
// 只有客户类型的用户,才进行数据权限的处理
if (!UserSystemEnum.CUSTOMER.equals(UserSystemEnum.getByKey(MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_USER_SYSTEM)))) {
return null;
}
// 获得数据权限
CustomerVisibleProductRespDTO customerVisibleDto = loginUser.getContext(CONTEXT_KEY, CustomerVisibleProductRespDTO.class);
// 从上下文中拿不到,则调用逻辑进行获取
if (customerVisibleDto == null) {
Long customerId = customerApi.queryCustomerIdByUserId(loginUser.getId());
customerVisibleDto = inquireCustomerApi.queryCustomerIdByCustomerId(customerId);
if (customerVisibleDto == null || CommonUtil.isEmpty(customerVisibleDto.getItems())) {
log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser));
throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限",
loginUser.getId(), tableName, tableAlias.getName()));
}
// 添加到上下文中,避免重复计算
loginUser.setContext(CONTEXT_KEY, customerVisibleDto);
}
Set<Long> productIds = CommonUtil.listConvertSet(customerVisibleDto.getItems(), CustomerVisibleProductRespDTO.CustomerProduct::getProductId);
Expression productIdsExpression = buildDeptExpression(tableName,tableAlias, productIds);
return new ParenthesedExpressionList(productIdsExpression);
}
private Expression buildDeptExpression(String tableName, Alias tableAlias, Set<Long> productIds) {
// 如果为空,则无条件
if (CollUtil.isEmpty(productIds)) {
return null;
}
// 拼接条件
return new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, PRODUCT_ID_COLUMN),
// Parenthesis 的目的,是提供 (1,2,3) 的 () 左右括号
new ParenthesedExpressionList(new ExpressionList<LongValue>(CollectionUtils.convertList(productIds, LongValue::new))));
}
}
......@@ -20,6 +20,7 @@ public class LoginUser {
public static final String INFO_KEY_NICKNAME = "nickname";
public static final String INFO_KEY_DEPT_ID = "deptId";
public static final String INFO_KEY_USER_SYSTEM = "userSystem";
/**
* 用户编号
......
......@@ -3,6 +3,7 @@ package cn.iocoder.foodnexus.framework.security.core.util;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.security.core.LoginUser;
import cn.iocoder.foodnexus.framework.web.core.util.WebFrameworkUtils;
import org.springframework.lang.Nullable;
......@@ -102,6 +103,11 @@ public class SecurityFrameworkUtils {
return loginUser != null ? MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_NICKNAME) : null;
}
public static UserSystemEnum getUserSystem() {
LoginUser loginUser = getLoginUser();
return UserSystemEnum.getByKey(MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_USER_SYSTEM));
}
/**
* 获得当前用户的部门编号,从上下文中
*
......
......@@ -27,6 +27,7 @@
<module>foodnexus-spring-boot-starter-biz-tenant</module>
<module>foodnexus-spring-boot-starter-biz-data-permission</module>
<module>foodnexus-spring-boot-starter-biz-customer-permission</module>
<module>foodnexus-spring-boot-starter-biz-ip</module>
</modules>
......
......@@ -5,7 +5,6 @@ import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
......
......@@ -25,4 +25,5 @@ public interface ErpSupplierApi {
Map<Long, String> queryNameMapByIds(Collection<Long> list);
String queryNameById(Long supplierId);
}
package cn.iocoder.foodnexus.module.erp.api.service;
/**
* @author : yanghao
* create at: 2025/9/5 17:25
* @description: 仓库api
*/
public interface ErpWarehouseApi {
boolean existsById(Long warehouseId);
}
package cn.iocoder.foodnexus.module.erp.api.vo.warehouse;
import lombok.Data;
import java.io.Serializable;
/**
* @author : yanghao
* create at: 2025/9/5 17:26
* @description: 仓库信息
*/
@Data
public class WarehouseInfo implements Serializable {
/**
* 仓库id(父级)
*/
private Long warehouseId;
/**
* 库区id(子级)
*/
private Long warehouseAreaId;
/**
* 仓库名称
*/
private String name;
/**
* 仓库地址
*/
private String address;
/**
* 负责人
*/
private String principal;
/**
* 联系电话
*/
private String telephone;
}
......@@ -66,4 +66,6 @@ public interface CustomerWarehouseService {
void deleteByCustomerId(Long customerId);
CustomerWarehouseDO getCustomerWarehouseByCustomerId(Long id);
boolean exists(Long warehouseAreaId, Long customerId);
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.erp.service.customerwarehouse;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
......@@ -97,4 +98,11 @@ public class CustomerWarehouseServiceImpl implements CustomerWarehouseService {
return customerWarehouseMapper.selectOne(CustomerWarehouseDO::getCustomerId, customerId);
}
@Override
public boolean exists(Long warehouseAreaId, Long customerId) {
return customerWarehouseMapper.exists(Wrappers.<CustomerWarehouseDO>lambdaQuery()
.eq(CustomerWarehouseDO::getCustomerId, customerId)
.eq(CustomerWarehouseDO::getWarehouseAreaId, warehouseAreaId));
}
}
\ No newline at end of file
......@@ -18,7 +18,7 @@ import cn.iocoder.foodnexus.module.erp.api.enums.ErpAuditStatus;
import cn.iocoder.foodnexus.module.system.controller.admin.vo.AuditCommonReqVO;
import cn.iocoder.foodnexus.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.foodnexus.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.module.system.service.dept.DeptService;
import cn.iocoder.foodnexus.module.system.service.user.AdminUserService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
......@@ -34,6 +34,7 @@ import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.module.erp.enums.ErrorCodeConstants.*;
......@@ -215,4 +216,9 @@ public class ErpSupplierServiceImpl implements ErpSupplierService, ErpSupplierAp
}
return CommonUtil.listConvertMap(supplierMapper.selectByIds(list), ErpSupplierDO::getId, ErpSupplierDO::getName);
}
@Override
public String queryNameById(Long supplierId) {
return Optional.ofNullable(supplierMapper.selectById(supplierId)).map(ErpSupplierDO::getName).orElse("");
}
}
......@@ -18,7 +18,7 @@ import cn.iocoder.foodnexus.module.erp.service.customerwarehouse.CustomerWarehou
import cn.iocoder.foodnexus.module.erp.service.stock.ErpWarehouseService;
import cn.iocoder.foodnexus.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.foodnexus.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.module.system.service.dept.DeptService;
import cn.iocoder.foodnexus.module.system.service.user.AdminUserService;
import com.google.common.collect.Maps;
......
......@@ -4,10 +4,12 @@ import cn.hutool.core.collection.CollUtil;
import cn.iocoder.foodnexus.framework.common.enums.CommonStatusEnum;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.module.erp.api.service.ErpWarehouseApi;
import cn.iocoder.foodnexus.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSaveReqVO;
import cn.iocoder.foodnexus.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.stock.ErpWarehouseDO;
import cn.iocoder.foodnexus.module.erp.dal.mysql.stock.ErpWarehouseMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -29,7 +31,7 @@ import static cn.iocoder.foodnexus.module.erp.enums.ErrorCodeConstants.*;
*/
@Service
@Validated
public class ErpWarehouseServiceImpl implements ErpWarehouseService {
public class ErpWarehouseServiceImpl implements ErpWarehouseService, ErpWarehouseApi {
@Resource
private ErpWarehouseMapper warehouseMapper;
......@@ -122,4 +124,9 @@ public class ErpWarehouseServiceImpl implements ErpWarehouseService {
return warehouseMapper.selectPage(pageReqVO);
}
@Override
public boolean existsById(Long warehouseId) {
return warehouseMapper.exists(Wrappers.<ErpWarehouseDO>lambdaQuery()
.eq(ErpWarehouseDO::getId, warehouseId));
}
}
......@@ -4,7 +4,6 @@ import cn.iocoder.foodnexus.framework.common.pojo.CommonResult;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.foodnexus.module.erp.api.service.ErpCustomerApi;
import cn.iocoder.foodnexus.module.operations.controller.admin.inquireprice.vo.InquirePriceRespVO;
import cn.iocoder.foodnexus.module.operations.controller.admin.inquirepriceitem.vo.InquirePriceItemRespVO;
......@@ -15,7 +14,7 @@ import cn.iocoder.foodnexus.module.operations.service.inquirecustomerpush.Inquir
import cn.iocoder.foodnexus.module.operations.service.inquireprice.InquirePriceService;
import cn.iocoder.foodnexus.module.operations.service.inquirepriceitem.InquirePriceItemService;
import cn.iocoder.foodnexus.module.system.annotations.AppSystemAuth;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
......
package cn.iocoder.foodnexus.module.operations.controller.app.inquiresupplierpush;
import cn.iocoder.foodnexus.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.foodnexus.framework.common.pojo.CommonResult;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.common.util.collection.MapUtils;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.framework.excel.core.util.ExcelUtils;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.foodnexus.module.erp.api.service.ErpSupplierApi;
import cn.iocoder.foodnexus.module.operations.controller.admin.inquiresupplierpush.vo.InquireSupplierPushPageReqVO;
import cn.iocoder.foodnexus.module.operations.controller.admin.inquiresupplierpush.vo.InquireSupplierPushRespVO;
import cn.iocoder.foodnexus.module.operations.controller.admin.inquiresupplierpush.vo.InquireSupplierPushSaveReqVO;
import cn.iocoder.foodnexus.module.operations.controller.app.inquiresupplierpush.vo.AppInquireSupplierPushConfirmReqVO;
import cn.iocoder.foodnexus.module.operations.controller.app.inquiresupplierpush.vo.AppInquireSupplierPushDetailsRespVO;
import cn.iocoder.foodnexus.module.operations.controller.app.inquiresupplierpush.vo.AppInquireSupplierPushPageReqVO;
import cn.iocoder.foodnexus.module.operations.controller.app.inquiresupplierpush.vo.AppInquireSupplierPushRespVO;
import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquirepriceitem.InquirePriceItemDO;
import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquiresupplierpush.InquireSupplierPushDO;
import cn.iocoder.foodnexus.module.operations.service.inquirepriceitem.InquirePriceItemService;
import cn.iocoder.foodnexus.module.operations.service.inquiresupplierpush.InquireSupplierPushService;
import cn.iocoder.foodnexus.module.system.annotations.AppSystemAuth;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
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.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static cn.iocoder.foodnexus.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.foodnexus.framework.common.pojo.CommonResult.success;
@Tag(name = "APP - 询价推送")
......
......@@ -52,4 +52,15 @@ public interface InquireCustomerPushMapper extends BaseMapperX<InquireCustomerPu
.orderByDesc(InquireCustomerPushDO::getId);
return selectJoinPage(reqVO, AppInquireCustomerPushRespVO.class, wrapperX);
}
default List<InquirePriceItemDO> queryConfirmPush(Long customerId) {
MPJLambdaWrapper<InquireCustomerPushDO> wrapper = new MPJLambdaWrapper<>();
wrapper.selectAll(InquirePriceItemDO.class);
wrapper.innerJoin(InquirePriceDO.class, InquirePriceDO::getId, InquireCustomerPushDO::getInquirePriceId);
wrapper.innerJoin(InquirePriceItemDO.class, InquirePriceItemDO::getInquireId, InquirePriceDO::getId);
wrapper.eq(InquireCustomerPushDO::getCustomerId, customerId);
wrapper.eq(InquireCustomerPushDO::getConfirm, Boolean.TRUE);
wrapper.eq(InquirePriceDO::getIsPush, Boolean.TRUE);
return selectJoinList(InquirePriceItemDO.class, wrapper);
}
}
\ No newline at end of file
......@@ -49,4 +49,11 @@ public interface InquireSupplierPushMapper extends BaseMapperX<InquireSupplierPu
wrapper.groupBy(InquirePriceDO::getId);
return selectJoinPage(reqVO, AppInquireSupplierPushRespVO.class, wrapper);
}
default List<InquireSupplierPushDO> queryQuoteByItemId(List<Long> itemIds) {
LambdaQueryWrapperX<InquireSupplierPushDO> wrapperX = new LambdaQueryWrapperX<>();
wrapperX.in(InquireSupplierPushDO::getInquirePriceItemId, itemIds);
wrapperX.eq(InquireSupplierPushDO::getConfirm, Boolean.TRUE);
return selectList(wrapperX);
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.operations.service.inquirecustomerpush;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquirepriceitem.InquirePriceItemDO;
import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquiresupplierpush.InquireSupplierPushDO;
import cn.iocoder.foodnexus.module.operations.dal.mysql.inquiresupplierpush.InquireSupplierPushMapper;
import cn.iocoder.foodnexus.module.product.api.dto.CustomerVisibleProductRespDTO;
import cn.iocoder.foodnexus.module.operations.controller.app.inquirecustomerpush.vo.AppInquireCustomerPushPageReqVO;
import cn.iocoder.foodnexus.module.operations.controller.app.inquirecustomerpush.vo.AppInquireCustomerPushRespVO;
import cn.iocoder.foodnexus.module.product.api.InquireCustomerApi;
import cn.iocoder.foodnexus.module.system.dal.redis.RedisKeyConstants;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.query.MPJQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import cn.iocoder.foodnexus.module.operations.controller.admin.inquirecustomerpush.vo.*;
import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquirecustomerpush.InquireCustomerPushDO;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.module.operations.dal.mysql.inquirecustomerpush.InquireCustomerPushMapper;
import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils.diffList;
import static cn.iocoder.foodnexus.module.operations.enums.ErrorCodeConstants.*;
/**
......@@ -34,11 +38,14 @@ import static cn.iocoder.foodnexus.module.operations.enums.ErrorCodeConstants.*;
*/
@Service
@Validated
public class InquireCustomerPushServiceImpl implements InquireCustomerPushService {
public class InquireCustomerPushServiceImpl implements InquireCustomerPushService, InquireCustomerApi {
@Resource
private InquireCustomerPushMapper inquireCustomerPushMapper;
@Autowired
private InquireSupplierPushMapper supplierPushMapper;
@Override
public Long createInquireCustomerPush(InquireCustomerPushSaveReqVO createReqVO) {
// 插入
......@@ -95,6 +102,7 @@ public class InquireCustomerPushServiceImpl implements InquireCustomerPushServic
* @param id
*/
@Override
@CacheEvict(cacheNames = RedisKeyConstants.CUSTOMER_VISIBLE_PRODUCT, allEntries = true)
public void confirm(Long id) {
validateInquireCustomerPushExists(id);
inquireCustomerPushMapper.update(Wrappers.<InquireCustomerPushDO>lambdaUpdate()
......@@ -115,4 +123,42 @@ public class InquireCustomerPushServiceImpl implements InquireCustomerPushServic
return inquireCustomerPushMapper.selectPage(pageReqVO);
}
@Override
@Cacheable(cacheNames = RedisKeyConstants.CUSTOMER_VISIBLE_PRODUCT, key = "#customerId", unless = "#result == null")
public CustomerVisibleProductRespDTO queryCustomerIdByCustomerId(Long customerId) {
List<InquirePriceItemDO> inquirePriceItemDOS = inquireCustomerPushMapper.queryConfirmPush(customerId);
if (CommonUtil.isEmpty(inquirePriceItemDOS)) {
return null;
}
Map<Long, List<InquirePriceItemDO>> productIdMap = CommonUtil.listConvertListMap(inquirePriceItemDOS, InquirePriceItemDO::getProductId);
List<InquirePriceItemDO> currentItemList = new ArrayList<>();
productIdMap.forEach((productId, list) -> {
if (list.size() == 1) {
currentItemList.add(list.get(0));
} else if (list.size() > 1) {
currentItemList.add(list.stream().max(Comparator.comparing(InquirePriceItemDO::getCreateTime)).get());
}
});
// 查询对应供应商的报价
List<InquireSupplierPushDO> supplierPushList = supplierPushMapper.queryQuoteByItemId(CommonUtil.listConvert(currentItemList, InquirePriceItemDO::getId));
if (CommonUtil.isEmpty(supplierPushList)) {
return null;
}
CustomerVisibleProductRespDTO dto = new CustomerVisibleProductRespDTO();
Map<Long, List<InquireSupplierPushDO>> supplierProductMap = CommonUtil.listConvertListMap(supplierPushList, InquireSupplierPushDO::getProductId);
supplierProductMap.forEach((productId, list) -> {
// TODO 根据供应商评分排序等....
if (CommonUtil.isNotEmpty(list)) {
InquireSupplierPushDO push = list.get(0);
dto.put(push.getId(), push.getProductId(), push.getSupplierQuote());
}
});
return dto;
}
}
\ No newline at end of file
......@@ -12,6 +12,7 @@ import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquirepriceitem.In
import cn.iocoder.foodnexus.module.operations.service.inquirecustomerpush.InquireCustomerPushService;
import cn.iocoder.foodnexus.module.operations.service.inquirepriceitem.InquirePriceItemService;
import cn.iocoder.foodnexus.module.operations.service.inquiresupplierpush.InquireSupplierPushService;
import cn.iocoder.foodnexus.module.product.api.ProductSpuApi;
import cn.iocoder.foodnexus.module.product.service.spu.ProductSpuService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
......
......@@ -11,8 +11,10 @@ import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquirecustomerpush
import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquirepriceitem.InquirePriceItemDO;
import cn.iocoder.foodnexus.module.operations.service.inquireprice.InquirePriceService;
import cn.iocoder.foodnexus.module.operations.service.inquirepriceitem.InquirePriceItemService;
import cn.iocoder.foodnexus.module.system.dal.redis.RedisKeyConstants;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
......@@ -48,9 +50,6 @@ public class InquireSupplierPushServiceImpl implements InquireSupplierPushServic
@Autowired
private InquirePriceItemService inquirePriceItemService;
@Autowired
private InquirePriceService inquirePriceService;
@Override
public Long createInquireSupplierPush(InquireSupplierPushSaveReqVO createReqVO) {
// 插入
......@@ -107,6 +106,7 @@ public class InquireSupplierPushServiceImpl implements InquireSupplierPushServic
* @param updateReqVO
*/
@Override
@CacheEvict(cacheNames = RedisKeyConstants.CUSTOMER_VISIBLE_PRODUCT, allEntries = true)
public void confirm(AppInquireSupplierPushConfirmReqVO updateReqVO) {
List<InquireSupplierPushDO> inquireSupplierPushDOS = inquireSupplierPushMapper.selectList(new LambdaQueryWrapperX<InquireSupplierPushDO>()
.eq(InquireSupplierPushDO::getSupplierId, updateReqVO.getSupplierId())
......
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-module-mall</artifactId>
<version>${revision}</version>
</parent>
<artifactId>foodnexus-module-order</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
客户订单
</description>
<dependencies>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-module-product</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-module-product-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-module-erp</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-module-operations</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</project>
package cn.iocoder.foodnexus.module.order.controller.admin.customerorder;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.*;
import jakarta.validation.*;
import jakarta.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.pojo.CommonResult;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import static cn.iocoder.foodnexus.framework.common.pojo.CommonResult.success;
import cn.iocoder.foodnexus.framework.excel.core.util.ExcelUtils;
import cn.iocoder.foodnexus.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.foodnexus.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorder.CustomerOrderDO;
import cn.iocoder.foodnexus.module.order.service.customerorder.CustomerOrderService;
@Tag(name = "管理后台 - 客户总订单")
@RestController
@RequestMapping("/order/customer-order")
@Validated
public class CustomerOrderController {
@Resource
private CustomerOrderService customerOrderService;
@PostMapping("/create")
@Operation(summary = "创建客户总订单")
@PreAuthorize("@ss.hasPermission('order:customer-order:create')")
public CommonResult<Long> createCustomerOrder(@Valid @RequestBody CustomerOrderSaveReqVO createReqVO) {
return success(customerOrderService.createCustomerOrder(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新客户总订单")
@PreAuthorize("@ss.hasPermission('order:customer-order:update')")
public CommonResult<Boolean> updateCustomerOrder(@Valid @RequestBody CustomerOrderSaveReqVO updateReqVO) {
customerOrderService.updateCustomerOrder(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除客户总订单")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('order:customer-order:delete')")
public CommonResult<Boolean> deleteCustomerOrder(@RequestParam("id") Long id) {
customerOrderService.deleteCustomerOrder(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除客户总订单")
@PreAuthorize("@ss.hasPermission('order:customer-order:delete')")
public CommonResult<Boolean> deleteCustomerOrderList(@RequestParam("ids") List<Long> ids) {
customerOrderService.deleteCustomerOrderListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得客户总订单")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('order:customer-order:query')")
public CommonResult<CustomerOrderRespVO> getCustomerOrder(@RequestParam("id") Long id) {
CustomerOrderDO customerOrder = customerOrderService.getCustomerOrder(id);
return success(BeanUtils.toBean(customerOrder, CustomerOrderRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得客户总订单分页")
@PreAuthorize("@ss.hasPermission('order:customer-order:query')")
public CommonResult<PageResult<CustomerOrderRespVO>> getCustomerOrderPage(@Valid CustomerOrderPageReqVO pageReqVO) {
PageResult<CustomerOrderDO> pageResult = customerOrderService.getCustomerOrderPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, CustomerOrderRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出客户总订单 Excel")
@PreAuthorize("@ss.hasPermission('order:customer-order:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportCustomerOrderExcel(@Valid CustomerOrderPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<CustomerOrderDO> list = customerOrderService.getCustomerOrderPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "客户总订单.xls", "数据", CustomerOrderRespVO.class,
BeanUtils.toBean(list, CustomerOrderRespVO.class));
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo;
import cn.iocoder.foodnexus.module.erp.api.vo.warehouse.WarehouseInfo;
import cn.iocoder.foodnexus.module.order.enums.CustomerOrderStatus;
import cn.iocoder.foodnexus.module.order.enums.DeliveryMode;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.foodnexus.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 客户总订单分页 Request VO")
@Data
public class CustomerOrderPageReqVO extends PageParam {
@Schema(description = "订单编号")
private String code;
@Schema(description = "订单状态", example = "2")
private CustomerOrderStatus orderStatus;
@Schema(description = "客户id", example = "20189")
private Long customerId;
@Schema(description = "收获仓库id", example = "27065")
private Long warehouseId;
@Schema(description = "收获库区id", example = "26507")
private Long warehouseAreaId;
@Schema(description = "仓库信息")
@TableField(typeHandler = Fastjson2TypeHandler.class)
private WarehouseInfo warehouseInfo;
@Schema(description = "配送模式")
private DeliveryMode deliveryMode;
@Schema(description = "采购订单数", example = "12099")
private Integer productCount;
@Schema(description = "供应商数", example = "8062")
private Integer supplierCount;
@Schema(description = "预计配送开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] planDeliveryStartTime;
@Schema(description = "预计配送结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] planDeliveryEndTime;
@Schema(description = "订单总金额(分)")
private Integer orderAmount;
@Schema(description = "实际支付总金额(分)")
private Integer actualAmount;
@Schema(description = "配送公司")
private String operCompany;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo;
import cn.iocoder.foodnexus.module.erp.api.vo.warehouse.WarehouseInfo;
import cn.iocoder.foodnexus.module.order.enums.CustomerOrderStatus;
import cn.iocoder.foodnexus.module.order.enums.DeliveryMode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import cn.idev.excel.annotation.*;
import cn.iocoder.foodnexus.framework.excel.core.annotations.DictFormat;
import cn.iocoder.foodnexus.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 客户总订单 Response VO")
@Data
@ExcelIgnoreUnannotated
public class CustomerOrderRespVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "19291")
@ExcelProperty("id")
private Long id;
@Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("订单编号")
private String code;
@Schema(description = "订单状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty(value = "订单状态", converter = DictConvert.class)
@DictFormat("order_customer_order_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private CustomerOrderStatus orderStatus;
@Schema(description = "客户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "20189")
@ExcelProperty("客户id")
private Long customerId;
@Schema(description = "收获仓库id", requiredMode = Schema.RequiredMode.REQUIRED, example = "27065")
@ExcelProperty("收获仓库id")
private Long warehouseId;
@Schema(description = "收获库区id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26507")
@ExcelProperty("收获库区id")
private Long warehouseAreaId;
@Schema(description = "仓库信息")
@ExcelProperty("仓库信息")
private WarehouseInfo warehouseInfo;
@Schema(description = "配送模式", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty(value = "配送模式", converter = DictConvert.class)
@DictFormat("order_delivery_mode") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private DeliveryMode deliveryMode;
@Schema(description = "采购订单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "12099")
@ExcelProperty("采购订单数")
private Integer productCount;
@Schema(description = "供应商数", example = "8062")
@ExcelProperty("供应商数")
private Integer supplierCount;
@Schema(description = "预计配送开始时间")
@ExcelProperty("预计配送开始时间")
private LocalDateTime planDeliveryStartTime;
@Schema(description = "预计配送结束时间")
@ExcelProperty("预计配送结束时间")
private LocalDateTime planDeliveryEndTime;
@Schema(description = "订单总金额(分)", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("订单总金额(分)")
private Integer orderAmount;
@Schema(description = "实际支付总金额(分)")
@ExcelProperty("实际支付总金额(分)")
private Integer actualAmount;
@Schema(description = "配送公司")
@ExcelProperty("配送公司")
private String operCompany;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo;
import cn.iocoder.foodnexus.module.erp.api.vo.warehouse.WarehouseInfo;
import cn.iocoder.foodnexus.module.order.enums.CustomerOrderStatus;
import cn.iocoder.foodnexus.module.order.enums.DeliveryMode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import jakarta.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 客户总订单新增/修改 Request VO")
@Data
public class CustomerOrderSaveReqVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "19291")
private Long id;
@Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "订单编号不能为空")
private String code;
@Schema(description = "订单状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotEmpty(message = "订单状态不能为空")
private CustomerOrderStatus orderStatus;
@Schema(description = "客户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "20189")
@NotNull(message = "客户id不能为空")
private Long customerId;
@Schema(description = "收获仓库id", requiredMode = Schema.RequiredMode.REQUIRED, example = "27065")
@NotNull(message = "收获仓库id不能为空")
private Long warehouseId;
@Schema(description = "收获库区id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26507")
@NotNull(message = "收获库区id不能为空")
private Long warehouseAreaId;
@Schema(description = "仓库信息")
private WarehouseInfo warehouseInfo;
@Schema(description = "配送模式", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "配送模式不能为空")
private DeliveryMode deliveryMode;
@Schema(description = "采购订单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "12099")
@NotNull(message = "采购订单数不能为空")
private Integer productCount;
@Schema(description = "供应商数", example = "8062")
private Integer supplierCount;
@Schema(description = "预计配送开始时间")
private LocalDateTime planDeliveryStartTime;
@Schema(description = "预计配送结束时间")
private LocalDateTime planDeliveryEndTime;
@Schema(description = "订单总金额(分)", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "订单总金额(分)不能为空")
private Integer orderAmount;
@Schema(description = "实际支付总金额(分)")
private Integer actualAmount;
@Schema(description = "配送公司")
private String operCompany;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.admin.customerorderitem;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.*;
import jakarta.validation.*;
import jakarta.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.pojo.CommonResult;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import static cn.iocoder.foodnexus.framework.common.pojo.CommonResult.success;
import cn.iocoder.foodnexus.framework.excel.core.util.ExcelUtils;
import cn.iocoder.foodnexus.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.foodnexus.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorderitem.vo.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorderitem.CustomerOrderItemDO;
import cn.iocoder.foodnexus.module.order.service.customerorderitem.CustomerOrderItemService;
@Tag(name = "管理后台 - 客户订单-子订单")
@RestController
@RequestMapping("/order/customer-order-item")
@Validated
public class CustomerOrderItemController {
@Resource
private CustomerOrderItemService customerOrderItemService;
@PostMapping("/create")
@Operation(summary = "创建客户订单-子订单")
@PreAuthorize("@ss.hasPermission('order:customer-order-item:create')")
public CommonResult<Long> createCustomerOrderItem(@Valid @RequestBody CustomerOrderItemSaveReqVO createReqVO) {
return success(customerOrderItemService.createCustomerOrderItem(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新客户订单-子订单")
@PreAuthorize("@ss.hasPermission('order:customer-order-item:update')")
public CommonResult<Boolean> updateCustomerOrderItem(@Valid @RequestBody CustomerOrderItemSaveReqVO updateReqVO) {
customerOrderItemService.updateCustomerOrderItem(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除客户订单-子订单")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('order:customer-order-item:delete')")
public CommonResult<Boolean> deleteCustomerOrderItem(@RequestParam("id") Long id) {
customerOrderItemService.deleteCustomerOrderItem(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除客户订单-子订单")
@PreAuthorize("@ss.hasPermission('order:customer-order-item:delete')")
public CommonResult<Boolean> deleteCustomerOrderItemList(@RequestParam("ids") List<Long> ids) {
customerOrderItemService.deleteCustomerOrderItemListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得客户订单-子订单")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('order:customer-order-item:query')")
public CommonResult<CustomerOrderItemRespVO> getCustomerOrderItem(@RequestParam("id") Long id) {
CustomerOrderItemDO customerOrderItem = customerOrderItemService.getCustomerOrderItem(id);
return success(BeanUtils.toBean(customerOrderItem, CustomerOrderItemRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得客户订单-子订单分页")
@PreAuthorize("@ss.hasPermission('order:customer-order-item:query')")
public CommonResult<PageResult<CustomerOrderItemRespVO>> getCustomerOrderItemPage(@Valid CustomerOrderItemPageReqVO pageReqVO) {
PageResult<CustomerOrderItemDO> pageResult = customerOrderItemService.getCustomerOrderItemPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, CustomerOrderItemRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出客户订单-子订单 Excel")
@PreAuthorize("@ss.hasPermission('order:customer-order-item:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportCustomerOrderItemExcel(@Valid CustomerOrderItemPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<CustomerOrderItemDO> list = customerOrderItemService.getCustomerOrderItemPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "客户订单-子订单.xls", "数据", CustomerOrderItemRespVO.class,
BeanUtils.toBean(list, CustomerOrderItemRespVO.class));
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.admin.customerorderitem.vo;
import cn.iocoder.foodnexus.module.product.api.dto.ProductInfo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.foodnexus.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 客户订单-子订单分页 Request VO")
@Data
public class CustomerOrderItemPageReqVO extends PageParam {
@Schema(description = "客户订单id", example = "15521")
private Long orderId;
@Schema(description = "客户id", example = "18288")
private Long customerId;
@Schema(description = "商品id", example = "9665")
private Long productId;
@Schema(description = "商品名称", example = "李四")
private String productName;
@Schema(description = "商品信息")
private ProductInfo productInfo;
@Schema(description = "商品对应供应商id", example = "19874")
private Long supplierId;
@Schema(description = "供应商名称", example = "芋艿")
private String supplierName;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "订单商品单价,单位:分", example = "29922")
private Integer orderItemPrice;
@Schema(description = "订单商品总价,单位:分")
private Integer orderItemTotal;
@Schema(description = "订单商品数量")
private Integer orderItemQuantity;
@Schema(description = "签收数量")
private Integer signedQuantity;
@Schema(description = "签收总价,单位:分")
private Integer signedTotal;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.admin.customerorderitem.vo;
import cn.iocoder.foodnexus.module.product.api.dto.ProductInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import cn.idev.excel.annotation.*;
@Schema(description = "管理后台 - 客户订单-子订单 Response VO")
@Data
@ExcelIgnoreUnannotated
public class CustomerOrderItemRespVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "22980")
@ExcelProperty("id")
private Long id;
@Schema(description = "客户订单id", requiredMode = Schema.RequiredMode.REQUIRED, example = "15521")
@ExcelProperty("客户订单id")
private Long orderId;
@Schema(description = "客户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "18288")
@ExcelProperty("客户id")
private Long customerId;
@Schema(description = "商品id", requiredMode = Schema.RequiredMode.REQUIRED, example = "9665")
@ExcelProperty("商品id")
private Long productId;
@Schema(description = "商品名称", example = "李四")
@ExcelProperty("商品名称")
private String productName;
@Schema(description = "商品信息")
@ExcelProperty("商品信息")
private ProductInfo productInfo;
@Schema(description = "商品对应供应商id", requiredMode = Schema.RequiredMode.REQUIRED, example = "19874")
@ExcelProperty("商品对应供应商id")
private Long supplierId;
@Schema(description = "供应商名称", example = "芋艿")
@ExcelProperty("供应商名称")
private String supplierName;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "订单商品单价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "29922")
@ExcelProperty("订单商品单价,单位:分")
private Integer orderItemPrice;
@Schema(description = "订单商品总价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("订单商品总价,单位:分")
private Integer orderItemTotal;
@Schema(description = "订单商品数量", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("订单商品数量")
private Integer orderItemQuantity;
@Schema(description = "签收数量", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("签收数量")
private Integer signedQuantity;
@Schema(description = "签收总价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("签收总价,单位:分")
private Integer signedTotal;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.admin.customerorderitem.vo;
import cn.iocoder.foodnexus.module.product.api.dto.ProductInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import jakarta.validation.constraints.*;
@Schema(description = "管理后台 - 客户订单-子订单新增/修改 Request VO")
@Data
public class CustomerOrderItemSaveReqVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "22980")
private Long id;
@Schema(description = "客户订单id", requiredMode = Schema.RequiredMode.REQUIRED, example = "15521")
@NotNull(message = "客户订单id不能为空")
private Long orderId;
@Schema(description = "客户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "18288")
@NotNull(message = "客户id不能为空")
private Long customerId;
@Schema(description = "商品id", requiredMode = Schema.RequiredMode.REQUIRED, example = "9665")
@NotNull(message = "商品id不能为空")
private Long productId;
@Schema(description = "商品名称", example = "李四")
private String productName;
@Schema(description = "商品信息")
private ProductInfo productInfo;
@Schema(description = "商品对应供应商id", requiredMode = Schema.RequiredMode.REQUIRED, example = "19874")
@NotNull(message = "商品对应供应商id不能为空")
private Long supplierId;
@Schema(description = "供应商名称", example = "芋艿")
private String supplierName;
@Schema(description = "订单商品单价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "29922")
@NotNull(message = "订单商品单价,单位:分不能为空")
private Integer orderItemPrice;
@Schema(description = "订单商品总价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "订单商品总价,单位:分不能为空")
private Integer orderItemTotal;
@Schema(description = "订单商品数量", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "订单商品数量不能为空")
private Integer orderItemQuantity;
@Schema(description = "签收数量", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "签收数量不能为空")
private Integer signedQuantity;
@Schema(description = "签收总价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "签收总价,单位:分不能为空")
private Integer signedTotal;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.app.customerOrder;
import cn.iocoder.foodnexus.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.pojo.CommonResult;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.framework.excel.core.util.ExcelUtils;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo.CustomerOrderPageReqVO;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo.CustomerOrderRespVO;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo.CustomerOrderSaveReqVO;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.AppCustomerOrderSaveReqVO;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorder.CustomerOrderDO;
import cn.iocoder.foodnexus.module.order.service.customerorder.CustomerOrderService;
import cn.iocoder.foodnexus.module.system.annotations.AppSystemAuth;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.foodnexus.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.foodnexus.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 客户总订单")
@RestController
@RequestMapping("/order/customer-order")
@Validated
@AppSystemAuth(UserSystemEnum.CUSTOMER)
public class AppCustomerOrderController {
@Resource
private CustomerOrderService customerOrderService;
@PostMapping("/create")
@Operation(summary = "创建客户总订单")
public CommonResult<Long> createCustomerOrder(@Valid @RequestBody AppCustomerOrderSaveReqVO createReqVO) {
return success(customerOrderService.appCreateCustomerOrder(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新客户总订单")
public CommonResult<Boolean> updateCustomerOrder(@Valid @RequestBody CustomerOrderSaveReqVO updateReqVO) {
customerOrderService.updateCustomerOrder(updateReqVO);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除客户总订单")
public CommonResult<Boolean> deleteCustomerOrderList(@RequestParam("ids") List<Long> ids) {
customerOrderService.deleteCustomerOrderListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得客户总订单")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
public CommonResult<CustomerOrderRespVO> getCustomerOrder(@RequestParam("id") Long id) {
CustomerOrderDO customerOrder = customerOrderService.getCustomerOrder(id);
return success(BeanUtils.toBean(customerOrder, CustomerOrderRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得客户总订单分页")
public CommonResult<PageResult<CustomerOrderRespVO>> getCustomerOrderPage(@Valid CustomerOrderPageReqVO pageReqVO) {
PageResult<CustomerOrderDO> pageResult = customerOrderService.getCustomerOrderPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, CustomerOrderRespVO.class));
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo;
import cn.iocoder.foodnexus.module.product.api.dto.ProductInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - 客户订单-子订单新增/修改 Request VO")
@Data
public class AppCustomerOrderItemSaveReqVO {
@Schema(description = "商品id", requiredMode = Schema.RequiredMode.REQUIRED, example = "9665")
@NotNull(message = "商品id不能为空")
private Long productId;
@Schema(description = "商品名称", example = "李四")
private String productName;
@Schema(description = "订单商品单价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "29922")
@NotNull(message = "订单商品单价,单位:分不能为空")
private Integer orderItemPrice;
@Schema(description = "订单商品总价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "订单商品总价,单位:分不能为空")
private Integer orderItemTotal;
@Schema(description = "订单商品数量", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "订单商品数量不能为空")
@Size(min = 1, message = "订单商品数量不能小于1")
private Integer orderItemQuantity;
@Schema(description = "商品对应供应商id", hidden = true)
private Long supplierId;
@Schema(description = "供应商名称", hidden = true)
private String supplierName;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo;
import cn.iocoder.foodnexus.framework.common.validation.InEnum;
import cn.iocoder.foodnexus.module.erp.api.vo.warehouse.WarehouseInfo;
import cn.iocoder.foodnexus.module.order.enums.CustomerOrderStatus;
import cn.iocoder.foodnexus.module.order.enums.DeliveryMode;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 客户总订单新增/修改 Request VO")
@Data
public class AppCustomerOrderSaveReqVO {
@Schema(description = "收获仓库id", requiredMode = Schema.RequiredMode.REQUIRED, example = "27065")
@NotNull(message = "收获仓库id不能为空")
private Long warehouseId;
@Schema(description = "收获库区id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26507")
@NotNull(message = "收获库区id不能为空")
private Long warehouseAreaId;
@Schema(description = "仓库信息")
private WarehouseInfo warehouseInfo;
@Schema(description = "配送模式", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "配送模式不能为空")
private DeliveryMode deliveryMode;
@Schema(description = "采购订单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "12099")
@NotNull(message = "采购订单数不能为空")
@Size(min = 1, message = "采购订单数不能小于1")
private Integer productCount;
@Schema(description = "预计配送开始时间")
private LocalDateTime planDeliveryStartTime;
@Schema(description = "预计配送结束时间")
private LocalDateTime planDeliveryEndTime;
@Schema(description = "配送公司")
private String operCompany;
@Schema(description = "orderItems")
@Valid
@NotNull(message = "订单子项不能为空")
@Size(min = 1, message = "订单子项不能为空")
private List<AppCustomerOrderItemSaveReqVO> orderItems;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.app.shoppingcart;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo.ShoppingCartPageReqVO;
import cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo.ShoppingCartRespVO;
import cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo.ShoppingCartSaveReqVO;
import cn.iocoder.foodnexus.module.system.annotations.AppSystemAuth;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.*;
import java.util.*;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.pojo.CommonResult;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import static cn.iocoder.foodnexus.framework.common.pojo.CommonResult.success;
import cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.shoppingcart.ShoppingCartDO;
import cn.iocoder.foodnexus.module.order.service.shoppingcart.ShoppingCartService;
@Tag(name = "管理后台 - 购物车")
@RestController
@RequestMapping("/order/shopping-cart")
@Validated
@AppSystemAuth(UserSystemEnum.CUSTOMER)
public class ShoppingCartController {
@Resource
private ShoppingCartService shoppingCartService;
@PostMapping("/create")
@Operation(summary = "创建购物车")
public CommonResult<Long> createShoppingCart(@Valid @RequestBody ShoppingCartSaveReqVO createReqVO) {
return success(shoppingCartService.createShoppingCart(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新购物车")
public CommonResult<Boolean> updateShoppingCart(@Valid @RequestBody ShoppingCartSaveReqVO updateReqVO) {
shoppingCartService.updateShoppingCart(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除购物车")
@Parameter(name = "id", description = "编号", required = true)
public CommonResult<Boolean> deleteShoppingCart(@RequestParam("id") Long id) {
shoppingCartService.deleteShoppingCart(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除购物车")
public CommonResult<Boolean> deleteShoppingCartList(@RequestParam("ids") List<Long> ids) {
shoppingCartService.deleteShoppingCartListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得购物车")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
public CommonResult<ShoppingCartRespVO> getShoppingCart(@RequestParam("id") Long id) {
ShoppingCartDO shoppingCart = shoppingCartService.getShoppingCart(id);
return success(BeanUtils.toBean(shoppingCart, ShoppingCartRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得购物车分页")
public CommonResult<PageResult<ShoppingCartRespVO>> getShoppingCartPage(@Valid ShoppingCartPageReqVO pageReqVO) {
pageReqVO.setCreator(SecurityFrameworkUtils.getLoginUserId());
PageResult<ShoppingCartDO> pageResult = shoppingCartService.getShoppingCartPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ShoppingCartRespVO.class));
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.foodnexus.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 购物车分页 Request VO")
@Data
public class ShoppingCartPageReqVO extends PageParam {
@Schema(description = "商品id", example = "7172")
private Long productId;
@Schema(description = "商品名称", example = "王五")
private String productName;
@Schema(description = "单价(分)", example = "6013")
private Integer unitPrice;
@Schema(description = "下单数量")
private Integer orderQuantity;
@Schema(description = "备注", example = "随便")
private String remark;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(hidden = true)
private Long creator;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import cn.idev.excel.annotation.*;
@Schema(description = "管理后台 - 购物车 Response VO")
@Data
@ExcelIgnoreUnannotated
public class ShoppingCartRespVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "12618")
@ExcelProperty("id")
private Long id;
@Schema(description = "商品id", requiredMode = Schema.RequiredMode.REQUIRED, example = "7172")
@ExcelProperty("商品id")
private Long productId;
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
@ExcelProperty("商品名称")
private String productName;
@Schema(description = "单价(分)", requiredMode = Schema.RequiredMode.REQUIRED, example = "6013")
@ExcelProperty("单价(分)")
private Integer unitPrice;
@Schema(description = "下单数量", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("下单数量")
private Integer orderQuantity;
@Schema(description = "备注", example = "随便")
@ExcelProperty("备注")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import jakarta.validation.constraints.*;
@Schema(description = "管理后台 - 购物车新增/修改 Request VO")
@Data
public class ShoppingCartSaveReqVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "12618")
private Long id;
@Schema(description = "商品id", requiredMode = Schema.RequiredMode.REQUIRED, example = "7172")
@NotNull(message = "商品id不能为空")
private Long productId;
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
@NotEmpty(message = "商品名称不能为空")
private String productName;
@Schema(description = "单价(分)", requiredMode = Schema.RequiredMode.REQUIRED, example = "6013")
@NotNull(message = "单价(分)不能为空")
private Integer unitPrice;
@Schema(description = "下单数量", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "下单数量不能为空")
private Integer orderQuantity;
@Schema(description = "备注", example = "随便")
private String remark;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.dal.dataobject.customerorder;
import cn.iocoder.foodnexus.module.erp.api.vo.warehouse.WarehouseInfo;
import cn.iocoder.foodnexus.module.order.enums.CustomerOrderStatus;
import cn.iocoder.foodnexus.module.order.enums.DeliveryMode;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.foodnexus.framework.mybatis.core.dataobject.BaseDO;
/**
* 客户总订单 DO
*
* @author 超级管理员
*/
@TableName(value = "order_customer_order", autoResultMap = true)
@KeySequence("order_customer_order_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CustomerOrderDO extends BaseDO {
/**
* id
*/
@TableId
private Long id;
/**
* 订单编号
*/
private String code;
/**
* 订单状态
*
* 枚举 {@link CustomerOrderStatus}
*/
private CustomerOrderStatus orderStatus;
/**
* 客户id
*/
private Long customerId;
/**
* 收获仓库id
*/
private Long warehouseId;
/**
* 收获库区id
*/
private Long warehouseAreaId;
/**
* 仓库信息
*/
private WarehouseInfo warehouseInfo;
/**
* 配送模式
*
* 枚举 {@link DeliveryMode}
*/
private DeliveryMode deliveryMode;
/**
* 采购订单数
*/
private Integer productCount;
/**
* 供应商数
*/
private Integer supplierCount;
/**
* 预计配送开始时间
*/
private LocalDateTime planDeliveryStartTime;
/**
* 预计配送结束时间
*/
private LocalDateTime planDeliveryEndTime;
/**
* 订单总金额(分)
*/
private Integer orderAmount;
/**
* 实际支付总金额(分)
*/
private Integer actualAmount;
/**
* 配送公司
*/
private String operCompany;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.dal.dataobject.customerorderitem;
import cn.iocoder.foodnexus.module.product.api.dto.ProductInfo;
import com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.foodnexus.framework.mybatis.core.dataobject.BaseDO;
/**
* 客户订单-子订单 DO
*
* @author 超级管理员
*/
@TableName(value = "order_customer_order_item", autoResultMap = true)
@KeySequence("order_customer_order_item_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CustomerOrderItemDO extends BaseDO {
/**
* id
*/
@TableId
private Long id;
/**
* 客户订单id
*/
private Long orderId;
/**
* 客户id
*/
private Long customerId;
/**
* 商品id
*/
private Long productId;
/**
* 商品名称
*/
private String productName;
/**
* 商品信息
*/
@TableField(typeHandler = Fastjson2TypeHandler.class)
private ProductInfo productInfo;
/**
* 商品对应供应商id
*/
private Long supplierId;
/**
* 供应商名称
*/
private String supplierName;
/**
* 订单商品单价,单位:分
*/
private Integer orderItemPrice;
/**
* 订单商品总价,单位:分
*/
private Integer orderItemTotal;
/**
* 订单商品数量
*/
private Integer orderItemQuantity;
/**
* 签收数量
*/
private Integer signedQuantity;
/**
* 签收总价,单位:分
*/
private Integer signedTotal;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.dal.dataobject.shoppingcart;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.foodnexus.framework.mybatis.core.dataobject.BaseDO;
/**
* 购物车 DO
*
* @author 超级管理员
*/
@TableName("order_shopping_cart")
@KeySequence("order_shopping_cart_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ShoppingCartDO extends BaseDO {
/**
* id
*/
@TableId
private Long id;
/**
* 商品id
*/
private Long productId;
/**
* 商品名称
*/
private String productName;
/**
* 单价(分)
*/
private Integer unitPrice;
/**
* 下单数量
*/
private Integer orderQuantity;
/**
* 备注
*/
private String remark;
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.dal.mysql.customerorder;
import java.util.*;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.foodnexus.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorder.CustomerOrderDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo.*;
/**
* 客户总订单 Mapper
*
* @author 超级管理员
*/
@Mapper
public interface CustomerOrderMapper extends BaseMapperX<CustomerOrderDO> {
default PageResult<CustomerOrderDO> selectPage(CustomerOrderPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<CustomerOrderDO>()
.eqIfPresent(CustomerOrderDO::getCode, reqVO.getCode())
.eqIfPresent(CustomerOrderDO::getOrderStatus, reqVO.getOrderStatus())
.eqIfPresent(CustomerOrderDO::getCustomerId, reqVO.getCustomerId())
.eqIfPresent(CustomerOrderDO::getWarehouseId, reqVO.getWarehouseId())
.eqIfPresent(CustomerOrderDO::getWarehouseAreaId, reqVO.getWarehouseAreaId())
.eqIfPresent(CustomerOrderDO::getWarehouseInfo, reqVO.getWarehouseInfo())
.eqIfPresent(CustomerOrderDO::getDeliveryMode, reqVO.getDeliveryMode())
.eqIfPresent(CustomerOrderDO::getProductCount, reqVO.getProductCount())
.eqIfPresent(CustomerOrderDO::getSupplierCount, reqVO.getSupplierCount())
.betweenIfPresent(CustomerOrderDO::getPlanDeliveryStartTime, reqVO.getPlanDeliveryStartTime())
.betweenIfPresent(CustomerOrderDO::getPlanDeliveryEndTime, reqVO.getPlanDeliveryEndTime())
.eqIfPresent(CustomerOrderDO::getOrderAmount, reqVO.getOrderAmount())
.eqIfPresent(CustomerOrderDO::getActualAmount, reqVO.getActualAmount())
.eqIfPresent(CustomerOrderDO::getOperCompany, reqVO.getOperCompany())
.betweenIfPresent(CustomerOrderDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(CustomerOrderDO::getId));
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.dal.mysql.customerorderitem;
import java.util.*;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.foodnexus.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorderitem.CustomerOrderItemDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorderitem.vo.*;
/**
* 客户订单-子订单 Mapper
*
* @author 超级管理员
*/
@Mapper
public interface CustomerOrderItemMapper extends BaseMapperX<CustomerOrderItemDO> {
default PageResult<CustomerOrderItemDO> selectPage(CustomerOrderItemPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<CustomerOrderItemDO>()
.eqIfPresent(CustomerOrderItemDO::getOrderId, reqVO.getOrderId())
.eqIfPresent(CustomerOrderItemDO::getCustomerId, reqVO.getCustomerId())
.eqIfPresent(CustomerOrderItemDO::getProductId, reqVO.getProductId())
.likeIfPresent(CustomerOrderItemDO::getProductName, reqVO.getProductName())
.eqIfPresent(CustomerOrderItemDO::getProductInfo, reqVO.getProductInfo())
.eqIfPresent(CustomerOrderItemDO::getSupplierId, reqVO.getSupplierId())
.likeIfPresent(CustomerOrderItemDO::getSupplierName, reqVO.getSupplierName())
.betweenIfPresent(CustomerOrderItemDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(CustomerOrderItemDO::getOrderItemPrice, reqVO.getOrderItemPrice())
.eqIfPresent(CustomerOrderItemDO::getOrderItemTotal, reqVO.getOrderItemTotal())
.eqIfPresent(CustomerOrderItemDO::getOrderItemQuantity, reqVO.getOrderItemQuantity())
.eqIfPresent(CustomerOrderItemDO::getSignedQuantity, reqVO.getSignedQuantity())
.eqIfPresent(CustomerOrderItemDO::getSignedTotal, reqVO.getSignedTotal())
.orderByDesc(CustomerOrderItemDO::getId));
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.dal.mysql.shoppingcart;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.foodnexus.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo.ShoppingCartPageReqVO;
import cn.iocoder.foodnexus.module.order.dal.dataobject.shoppingcart.ShoppingCartDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 购物车 Mapper
*
* @author 超级管理员
*/
@Mapper
public interface ShoppingCartMapper extends BaseMapperX<ShoppingCartDO> {
default PageResult<ShoppingCartDO> selectPage(ShoppingCartPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ShoppingCartDO>()
.eqIfPresent(ShoppingCartDO::getProductId, reqVO.getProductId())
.likeIfPresent(ShoppingCartDO::getProductName, reqVO.getProductName())
.eqIfPresent(ShoppingCartDO::getUnitPrice, reqVO.getUnitPrice())
.eqIfPresent(ShoppingCartDO::getOrderQuantity, reqVO.getOrderQuantity())
.eqIfPresent(ShoppingCartDO::getRemark, reqVO.getRemark())
.betweenIfPresent(ShoppingCartDO::getCreateTime, reqVO.getCreateTime())
.eq(ShoppingCartDO::getCreator, reqVO.getCreator())
.orderByDesc(ShoppingCartDO::getId));
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.enums;
import cn.iocoder.foodnexus.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* @author : yanghao
* create at: 2025/9/5 17:12
* @description: 客户 - 订单 - 状态
*/
@Getter
@AllArgsConstructor
public enum CustomerOrderStatus implements ArrayValuable<String> {
// 下单成功
ORDER_SUCCESS("下单成功", "order_success"),
// 订单匹配
ORDER_MATCH("订单匹配", "order_match"),
// 供应商接单
SUPPLIER_ACCEPT_ORDER("供应商接单", "supplier_accept_order"),
// 供应商发货
SUPPLIER_SHIP("供应商发货", "supplier_ship"),
// 供应商到货
SUPPLIER_ARRIVE("供应商到货", "supplier_arrive"),
;
private final String label;
private final String key;
public static final String[] ARRAYS = Arrays.stream(values()).map(CustomerOrderStatus::getKey).toArray(String[]::new);
/**
* @return 数组
*/
@Override
public String[] array() {
return ARRAYS;
}
}
package cn.iocoder.foodnexus.module.order.enums;
import cn.iocoder.foodnexus.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* @author : yanghao
* create at: 2025/9/5 17:16
* @description: 客户 - 订单 - 配送模式
*/
@Getter
@AllArgsConstructor
public enum DeliveryMode implements ArrayValuable<String> {
NORMAL("普通配送", "NORMAL"),
PRESSING("紧急配送", "PRESSING"),
;
private final String label;
private final String key;
public static final String[] ARRAYS = Arrays.stream(values()).map(DeliveryMode::getKey).toArray(String[]::new);
/**
* @return 数组
*/
@Override
public String[] array() {
return ARRAYS;
}
}
package cn.iocoder.foodnexus.module.order.enums;
import cn.iocoder.foodnexus.framework.common.exception.ErrorCode;
/**
* Product 错误码枚举类
*
* product 系统,使用 1-008-000-000 段
*/
public interface ErrorCodeConstants {
// ========== 购物车 1_018-000-000 ==========
ErrorCode SHOPPING_CART_NOT_EXISTS = new ErrorCode(1_018_100_000, "购物车不存在");
// ========== 客户总订单 1_019_100_0001 ==========
ErrorCode CUSTOMER_ORDER_NOT_EXISTS = new ErrorCode(1_019_100_001, "客户总订单不存在");
ErrorCode CUSTOMER_ORDER_WAREHOUSE_NOEXISTS = new ErrorCode(1_019_100_002, "仓库不存在");
ErrorCode CUSTOMER_WAREHOUSE_NOT_BIND = new ErrorCode(1_019_100_003, "所选仓库未绑定");
// ========== 客户订单-子订单 1_020_100_0001 ==========
ErrorCode CUSTOMER_ORDER_ITEM_NOT_EXISTS = new ErrorCode(1_020_100_001, "客户订单-子订单不存在");
}
package cn.iocoder.foodnexus.module.order.service.customerorder;
import java.util.*;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.AppCustomerOrderSaveReqVO;
import jakarta.validation.*;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorder.CustomerOrderDO;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
/**
* 客户总订单 Service 接口
*
* @author 超级管理员
*/
public interface CustomerOrderService {
/**
* 创建客户总订单
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createCustomerOrder(@Valid CustomerOrderSaveReqVO createReqVO);
/**
* 更新客户总订单
*
* @param updateReqVO 更新信息
*/
void updateCustomerOrder(@Valid CustomerOrderSaveReqVO updateReqVO);
/**
* 删除客户总订单
*
* @param id 编号
*/
void deleteCustomerOrder(Long id);
/**
* 批量删除客户总订单
*
* @param ids 编号
*/
void deleteCustomerOrderListByIds(List<Long> ids);
/**
* 获得客户总订单
*
* @param id 编号
* @return 客户总订单
*/
CustomerOrderDO getCustomerOrder(Long id);
/**
* 获得客户总订单分页
*
* @param pageReqVO 分页查询
* @return 客户总订单分页
*/
PageResult<CustomerOrderDO> getCustomerOrderPage(CustomerOrderPageReqVO pageReqVO);
/**
* 客户创建订单
* @param createReqVO
* @return
*/
Long appCreateCustomerOrder(AppCustomerOrderSaveReqVO createReqVO);
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.service.customerorder;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.security.core.LoginUser;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.module.customerpermission.core.annotation.CustomerVisible;
import cn.iocoder.foodnexus.module.erp.api.service.ErpCustomerApi;
import cn.iocoder.foodnexus.module.erp.api.service.ErpSupplierApi;
import cn.iocoder.foodnexus.module.erp.api.service.ErpWarehouseApi;
import cn.iocoder.foodnexus.module.erp.service.customerwarehouse.CustomerWarehouseService;
import cn.iocoder.foodnexus.module.operations.dal.dataobject.inquiresupplierpush.InquireSupplierPushDO;
import cn.iocoder.foodnexus.module.operations.service.inquiresupplierpush.InquireSupplierPushService;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.AppCustomerOrderItemSaveReqVO;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.AppCustomerOrderSaveReqVO;
import cn.iocoder.foodnexus.module.order.service.customerorderitem.CustomerOrderItemService;
import cn.iocoder.foodnexus.module.product.api.InquireCustomerApi;
import cn.iocoder.foodnexus.module.product.api.dto.CustomerVisibleProductRespDTO;
import cn.iocoder.foodnexus.module.system.annotations.AppSystemAuth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorder.vo.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorder.CustomerOrderDO;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.module.order.dal.mysql.customerorder.CustomerOrderMapper;
import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils.diffList;
import static cn.iocoder.foodnexus.module.order.enums.ErrorCodeConstants.*;
/**
* 客户总订单 Service 实现类
*
* @author 超级管理员
*/
@Service
@Validated
public class CustomerOrderServiceImpl implements CustomerOrderService {
@Resource
private CustomerOrderMapper customerOrderMapper;
@Autowired
private CustomerOrderItemService customerOrderItemService;
@Autowired
private CustomerWarehouseService customerWarehouseService;
@Autowired
private InquireCustomerApi inquireCustomerApi;
@Autowired
private ErpCustomerApi customerApi;
@Autowired
private ErpWarehouseApi warehouseApi;
@Autowired
private InquireSupplierPushService inquireSupplierPushService;
@Autowired
private ErpSupplierApi supplierApi;
@Override
public Long createCustomerOrder(CustomerOrderSaveReqVO createReqVO) {
// 插入
CustomerOrderDO customerOrder = BeanUtils.toBean(createReqVO, CustomerOrderDO.class);
customerOrderMapper.insert(customerOrder);
// 返回
return customerOrder.getId();
}
@Override
public void updateCustomerOrder(CustomerOrderSaveReqVO updateReqVO) {
// 校验存在
validateCustomerOrderExists(updateReqVO.getId());
// 更新
CustomerOrderDO updateObj = BeanUtils.toBean(updateReqVO, CustomerOrderDO.class);
customerOrderMapper.updateById(updateObj);
}
@Override
public void deleteCustomerOrder(Long id) {
// 校验存在
validateCustomerOrderExists(id);
// 删除
customerOrderMapper.deleteById(id);
}
@Override
public void deleteCustomerOrderListByIds(List<Long> ids) {
// 删除
customerOrderMapper.deleteByIds(ids);
}
private void validateCustomerOrderExists(Long id) {
if (customerOrderMapper.selectById(id) == null) {
throw exception(CUSTOMER_ORDER_NOT_EXISTS);
}
}
@Override
public CustomerOrderDO getCustomerOrder(Long id) {
return customerOrderMapper.selectById(id);
}
@Override
public PageResult<CustomerOrderDO> getCustomerOrderPage(CustomerOrderPageReqVO pageReqVO) {
return customerOrderMapper.selectPage(pageReqVO);
}
/**
* 客户创建订单
*
* @param createReqVO
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
@CustomerVisible
public Long appCreateCustomerOrder(AppCustomerOrderSaveReqVO createReqVO) {
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
this.validCustomerOrder(createReqVO, loginUser.getId());
Long customerId = customerApi.queryCustomerIdByUserId(loginUser.getId());
CustomerVisibleProductRespDTO dto = inquireCustomerApi.queryCustomerIdByCustomerId(customerId);
Map<Long, CustomerVisibleProductRespDTO.CustomerProduct> customerProductMap = CommonUtil.listConvertMap(dto.getItems(), CustomerVisibleProductRespDTO.CustomerProduct::getProductId);
for (AppCustomerOrderItemSaveReqVO item : createReqVO.getOrderItems()) {
if (customerProductMap.containsKey(item.getProductId())) {
CustomerVisibleProductRespDTO.CustomerProduct customerProduct = customerProductMap.get(item.getProductId());
item.setOrderItemPrice(customerProduct.getSupplierQuote());
item.setOrderItemTotal(customerProduct.getSupplierQuote() * item.getOrderItemQuantity());
InquireSupplierPushDO inquireSupplierPush = inquireSupplierPushService.getInquireSupplierPush(customerProduct.getInquireSupplierId());
item.setSupplierId(inquireSupplierPush.getSupplierId());
item.setSupplierName(supplierApi.queryNameById(inquireSupplierPush.getSupplierId()));
}
}
Long customerOrderId = this.createCustomerOrder(BeanUtils.toBean(createReqVO, CustomerOrderSaveReqVO.class));
customerOrderItemService.createBatch(createReqVO.getOrderItems(), customerOrderId, customerId);
return customerOrderId;
}
private void validCustomerOrder(AppCustomerOrderSaveReqVO createReqVO, Long customerId) {
if (!warehouseApi.existsById(createReqVO.getWarehouseId())) {
throw exception(CUSTOMER_ORDER_WAREHOUSE_NOEXISTS);
}
if (!warehouseApi.existsById(createReqVO.getWarehouseAreaId())) {
throw exception(CUSTOMER_ORDER_WAREHOUSE_NOEXISTS);
}
if (!customerWarehouseService.exists(createReqVO.getWarehouseAreaId(), customerId)) {
throw exception(CUSTOMER_WAREHOUSE_NOT_BIND);
}
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.service.customerorderitem;
import java.util.*;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.AppCustomerOrderItemSaveReqVO;
import jakarta.validation.*;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorderitem.vo.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorderitem.CustomerOrderItemDO;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
/**
* 客户订单-子订单 Service 接口
*
* @author 超级管理员
*/
public interface CustomerOrderItemService {
/**
* 创建客户订单-子订单
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createCustomerOrderItem(@Valid CustomerOrderItemSaveReqVO createReqVO);
/**
* 更新客户订单-子订单
*
* @param updateReqVO 更新信息
*/
void updateCustomerOrderItem(@Valid CustomerOrderItemSaveReqVO updateReqVO);
/**
* 删除客户订单-子订单
*
* @param id 编号
*/
void deleteCustomerOrderItem(Long id);
/**
* 批量删除客户订单-子订单
*
* @param ids 编号
*/
void deleteCustomerOrderItemListByIds(List<Long> ids);
/**
* 获得客户订单-子订单
*
* @param id 编号
* @return 客户订单-子订单
*/
CustomerOrderItemDO getCustomerOrderItem(Long id);
/**
* 获得客户订单-子订单分页
*
* @param pageReqVO 分页查询
* @return 客户订单-子订单分页
*/
PageResult<CustomerOrderItemDO> getCustomerOrderItemPage(CustomerOrderItemPageReqVO pageReqVO);
void createBatch(List<AppCustomerOrderItemSaveReqVO> orderItems, Long customerOrderId, Long customerId);
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.service.customerorderitem;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.foodnexus.module.erp.service.product.ErpProductService;
import cn.iocoder.foodnexus.module.order.controller.app.customerOrder.vo.AppCustomerOrderItemSaveReqVO;
import cn.iocoder.foodnexus.module.product.api.dto.ProductInfo;
import cn.iocoder.foodnexus.module.product.service.spu.ProductSpuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.foodnexus.module.order.controller.admin.customerorderitem.vo.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.customerorderitem.CustomerOrderItemDO;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.pojo.PageParam;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.module.order.dal.mysql.customerorderitem.CustomerOrderItemMapper;
import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils.diffList;
import static cn.iocoder.foodnexus.module.order.enums.ErrorCodeConstants.*;
/**
* 客户订单-子订单 Service 实现类
*
* @author 超级管理员
*/
@Service
@Validated
public class CustomerOrderItemServiceImpl implements CustomerOrderItemService {
@Resource
private CustomerOrderItemMapper customerOrderItemMapper;
@Autowired
private ProductSpuService productSpuService;
@Override
public Long createCustomerOrderItem(CustomerOrderItemSaveReqVO createReqVO) {
// 插入
CustomerOrderItemDO customerOrderItem = BeanUtils.toBean(createReqVO, CustomerOrderItemDO.class);
customerOrderItemMapper.insert(customerOrderItem);
// 返回
return customerOrderItem.getId();
}
@Override
public void updateCustomerOrderItem(CustomerOrderItemSaveReqVO updateReqVO) {
// 校验存在
validateCustomerOrderItemExists(updateReqVO.getId());
// 更新
CustomerOrderItemDO updateObj = BeanUtils.toBean(updateReqVO, CustomerOrderItemDO.class);
customerOrderItemMapper.updateById(updateObj);
}
@Override
public void deleteCustomerOrderItem(Long id) {
// 校验存在
validateCustomerOrderItemExists(id);
// 删除
customerOrderItemMapper.deleteById(id);
}
@Override
public void deleteCustomerOrderItemListByIds(List<Long> ids) {
// 删除
customerOrderItemMapper.deleteByIds(ids);
}
private void validateCustomerOrderItemExists(Long id) {
if (customerOrderItemMapper.selectById(id) == null) {
throw exception(CUSTOMER_ORDER_ITEM_NOT_EXISTS);
}
}
@Override
public CustomerOrderItemDO getCustomerOrderItem(Long id) {
return customerOrderItemMapper.selectById(id);
}
@Override
public PageResult<CustomerOrderItemDO> getCustomerOrderItemPage(CustomerOrderItemPageReqVO pageReqVO) {
return customerOrderItemMapper.selectPage(pageReqVO);
}
@Override
public void createBatch(List<AppCustomerOrderItemSaveReqVO> orderItems, Long customerOrderId, Long customerId) {
List<CustomerOrderItemDO> items = BeanUtils.toBean(orderItems, CustomerOrderItemDO.class, item -> {
item.setOrderId(customerOrderId);
item.setCustomerId(customerId);
item.setProductInfo(BeanUtils.toBean(productSpuService.getSpu(item.getProductId()),
ProductInfo.class));
item.setSignedQuantity(0);
item.setSignedTotal(0);
});
customerOrderItemMapper.insertBatch(items);
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.service.shoppingcart;
import java.util.*;
import cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo.ShoppingCartPageReqVO;
import cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo.ShoppingCartSaveReqVO;
import jakarta.validation.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.shoppingcart.ShoppingCartDO;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
/**
* 购物车 Service 接口
*
* @author 超级管理员
*/
public interface ShoppingCartService {
/**
* 创建购物车
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createShoppingCart(@Valid ShoppingCartSaveReqVO createReqVO);
/**
* 更新购物车
*
* @param updateReqVO 更新信息
*/
void updateShoppingCart(@Valid ShoppingCartSaveReqVO updateReqVO);
/**
* 删除购物车
*
* @param id 编号
*/
void deleteShoppingCart(Long id);
/**
* 批量删除购物车
*
* @param ids 编号
*/
void deleteShoppingCartListByIds(List<Long> ids);
/**
* 获得购物车
*
* @param id 编号
* @return 购物车
*/
ShoppingCartDO getShoppingCart(Long id);
/**
* 获得购物车分页
*
* @param pageReqVO 分页查询
* @return 购物车分页
*/
PageResult<ShoppingCartDO> getShoppingCartPage(ShoppingCartPageReqVO pageReqVO);
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.order.service.shoppingcart;
import cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo.ShoppingCartPageReqVO;
import cn.iocoder.foodnexus.module.order.controller.app.shoppingcart.vo.ShoppingCartSaveReqVO;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import java.util.*;
import cn.iocoder.foodnexus.module.order.dal.dataobject.shoppingcart.ShoppingCartDO;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.module.order.dal.mysql.shoppingcart.ShoppingCartMapper;
import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.foodnexus.module.order.enums.ErrorCodeConstants.*;
/**
* 购物车 Service 实现类
*
* @author 超级管理员
*/
@Service
@Validated
public class ShoppingCartServiceImpl implements ShoppingCartService {
@Resource
private ShoppingCartMapper shoppingCartMapper;
@Override
public Long createShoppingCart(ShoppingCartSaveReqVO createReqVO) {
// 插入
ShoppingCartDO shoppingCart = BeanUtils.toBean(createReqVO, ShoppingCartDO.class);
shoppingCartMapper.insert(shoppingCart);
// 返回
return shoppingCart.getId();
}
@Override
public void updateShoppingCart(ShoppingCartSaveReqVO updateReqVO) {
// 校验存在
validateShoppingCartExists(updateReqVO.getId());
// 更新
ShoppingCartDO updateObj = BeanUtils.toBean(updateReqVO, ShoppingCartDO.class);
shoppingCartMapper.updateById(updateObj);
}
@Override
public void deleteShoppingCart(Long id) {
// 校验存在
validateShoppingCartExists(id);
// 删除
shoppingCartMapper.deleteById(id);
}
@Override
public void deleteShoppingCartListByIds(List<Long> ids) {
// 删除
shoppingCartMapper.deleteByIds(ids);
}
private void validateShoppingCartExists(Long id) {
if (shoppingCartMapper.selectById(id) == null) {
throw exception(SHOPPING_CART_NOT_EXISTS);
}
}
@Override
public ShoppingCartDO getShoppingCart(Long id) {
return shoppingCartMapper.selectById(id);
}
@Override
public PageResult<ShoppingCartDO> getShoppingCartPage(ShoppingCartPageReqVO pageReqVO) {
return shoppingCartMapper.selectPage(pageReqVO);
}
}
\ No newline at end of file
package cn.iocoder.foodnexus.module.product.api;
import cn.iocoder.foodnexus.module.product.api.dto.CustomerVisibleProductRespDTO;
/**
* @author : yanghao
* create at: 2025/9/4 17:02
* @description: 客户 - 询价
*/
public interface InquireCustomerApi {
CustomerVisibleProductRespDTO queryCustomerIdByCustomerId(Long customerId);
}
package cn.iocoder.foodnexus.module.product.api.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @author : yanghao
* create at: 2025/9/4 11:49
* @description: 客户可见商品集合
*/
@Data
public class CustomerVisibleProductRespDTO implements Serializable {
public CustomerVisibleProductRespDTO() {
this.items = new ArrayList<>();
}
private List<CustomerProduct> items;
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class CustomerProduct implements Serializable {
/**
* 供应商 - 报价
*/
private Long inquireSupplierId;
/**
* 商品id
*/
private Long productId;
/**
* 供应商报价
*/
private Integer supplierQuote;
}
public void put(Long inquireSupplierId, Long productId, Integer supplierQuote) {
this.items.add(new CustomerProduct(inquireSupplierId, productId, supplierQuote));
}
}
package cn.iocoder.foodnexus.module.product.api.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author : yanghao
* create at: 2025/9/5 17:32
* @description: 商品信息
*/
@Data
public class ProductInfo implements Serializable {
/**
* 商品 SPU 编号,自增
*/
private Long id;
// ========== 基本信息 =========
/**
* 商品名称
*/
private String name;
/**
* 商品规格
*/
private String introduction;
/**
* 商品详情
*/
private String description;
/**
* 商品分类编号
*
*/
private Long categoryId;
/**
* 商品封面图
*/
private String picUrl;
/**
* 排序字段
*/
private Integer sort;
/**
* 商品单位
*/
private String unitName;
}
......@@ -51,8 +51,8 @@ public class ProductBrowseHistoryController {
convertSet(pageResult.getList(), ProductBrowseHistoryDO::getSpuId));
return success(BeanUtils.toBean(pageResult, ProductBrowseHistoryRespVO.class,
vo -> Optional.ofNullable(spuMap.get(vo.getSpuId()))
.ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setPrice(spu.getPrice())
.setSalesCount(spu.getSalesCount()).setStock(spu.getStock()))));
.ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl())
.setSalesCount(spu.getSalesCount()))));
}
}
\ No newline at end of file
......@@ -25,12 +25,8 @@ public class ProductSpuRespVO {
@ExcelProperty("商品名称")
private String name;
@Schema(description = "关键字", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉丝滑不出汗")
@ExcelProperty("关键字")
private String keyword;
@Schema(description = "商品简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖简介")
@ExcelProperty("商品简介")
@Schema(description = "商品规格", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖简介")
@ExcelProperty("商品规格")
private String introduction;
@Schema(description = "商品详情", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖详情")
......@@ -41,9 +37,6 @@ public class ProductSpuRespVO {
@ExcelProperty("商品分类编号")
private Long categoryId;
@Schema(description = "商品品牌编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("商品品牌编号")
private Long brandId;
@Schema(description = "商品封面图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
@ExcelProperty("商品封面图")
......@@ -65,49 +58,8 @@ public class ProductSpuRespVO {
@ExcelProperty("创建时间")
private LocalDateTime createTime;
// ========== SKU 相关字段 =========
@Schema(description = "规格类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@ExcelProperty("规格类型")
private Boolean specType;
@Schema(description = "商品价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "1999")
@ExcelProperty(value = "商品价格", converter = MoneyConvert.class)
private Integer price;
@Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "199")
@ExcelProperty(value = "市场价", converter = MoneyConvert.class)
private Integer marketPrice;
@Schema(description = "成本价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "19")
@ExcelProperty(value = "成本价", converter = MoneyConvert.class)
private Integer costPrice;
@Schema(description = "商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
@ExcelProperty("库存")
private Integer stock;
@Schema(description = "SKU 数组")
private List<ProductSkuRespVO> skus;
// ========== 物流相关字段 =========
@Schema(description = "配送方式数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private List<Integer> deliveryTypes;
@Schema(description = "物流配置模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "111")
@ExcelProperty("物流配置模板编号")
private Long deliveryTemplateId;
// ========== 营销相关字段 =========
@Schema(description = "赠送积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "111")
@ExcelProperty("赠送积分")
private Integer giveIntegral;
@Schema(description = "分销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@ExcelProperty("分销类型")
private Boolean subCommissionType;
@Schema(description = "商品单位")
private String unitName;
// ========== 统计相关字段 =========
......@@ -123,4 +75,16 @@ public class ProductSpuRespVO {
@ExcelProperty("商品点击量")
private Integer browseCount;
@Schema(description = "审核状态")
private String auditStatus;
@Schema(description = "审核人(关联用户ID)")
private Long auditor;
@Schema(description = "审核时间")
private LocalDateTime auditTime;
@Schema(description = "审核意见")
private String auditReason;
}
......@@ -19,7 +19,7 @@ public class ProductSpuSaveReqVO {
@NotEmpty(message = "商品名称不能为空")
private String name;
@Schema(description = "商品简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖简介")
@Schema(description = "商品规格", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖简介")
private String introduction;
@Schema(description = "商品详情", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖详情")
......@@ -40,6 +40,9 @@ public class ProductSpuSaveReqVO {
@NotNull(message = "商品排序字段不能为空")
private Integer sort;
@Schema(description = "商品单位")
private String unitName;
// ========== 统计相关字段 =========
@Schema(description = "虚拟销量", example = "66")
......
......@@ -15,18 +15,6 @@ public class ProductSpuSimpleRespVO {
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖")
private String name;
@Schema(description = "商品价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1999")
private Integer price;
@Schema(description = "商品市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "199")
private Integer marketPrice;
@Schema(description = "商品成本价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "19")
private Integer costPrice;
@Schema(description = "商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000")
private Integer stock;
// ========== 统计相关字段 =========
@Schema(description = "商品销量", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
......
......@@ -67,8 +67,8 @@ public class AppProductBrowseHistoryController {
Map<Long, ProductSpuDO> spuMap = convertMap(productSpuService.getSpuList(spuIds), ProductSpuDO::getId);
return success(BeanUtils.toBean(pageResult, AppProductBrowseHistoryRespVO.class,
vo -> Optional.ofNullable(spuMap.get(vo.getSpuId()))
.ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setPrice(spu.getPrice())
.setSalesCount(spu.getSalesCount()).setStock(spu.getStock()))));
.ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl())
.setSalesCount(spu.getSalesCount()))));
}
}
......@@ -7,12 +7,12 @@ import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
import cn.iocoder.foodnexus.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.foodnexus.module.product.controller.app.spu.vo.AppProductSpuRespVO;
import cn.iocoder.foodnexus.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.foodnexus.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.foodnexus.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.foodnexus.module.product.service.history.ProductBrowseHistoryService;
import cn.iocoder.foodnexus.module.product.service.sku.ProductSkuService;
import cn.iocoder.foodnexus.module.product.service.spu.ProductSpuService;
import cn.iocoder.foodnexus.module.system.annotations.AppSystemAuth;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
......@@ -39,34 +39,16 @@ import static cn.iocoder.foodnexus.module.product.enums.ErrorCodeConstants.SPU_N
@RestController
@RequestMapping("/product/spu")
@Validated
@AppSystemAuth({UserSystemEnum.CUSTOMER, UserSystemEnum.SUPPLIER, UserSystemEnum.DELIVERY})
public class AppProductSpuController {
@Resource
private ProductSpuService productSpuService;
@Resource
private ProductSkuService productSkuService;
@Resource
private ProductBrowseHistoryService productBrowseHistoryService;
@GetMapping("/list-by-ids")
@Operation(summary = "获得商品 SPU 列表")
@Parameter(name = "ids", description = "编号列表", required = true)
@PermitAll
public CommonResult<List<AppProductSpuRespVO>> getSpuList(@RequestParam("ids") Set<Long> ids) {
List<ProductSpuDO> list = productSpuService.getSpuList(ids);
if (CollUtil.isEmpty(list)) {
return success(Collections.emptyList());
}
// 拼接返回
list.forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
List<AppProductSpuRespVO> voList = BeanUtils.toBean(list, AppProductSpuRespVO.class);
return success(voList);
}
@GetMapping("/page")
@Operation(summary = "获得商品 SPU 分页")
@PermitAll
public CommonResult<PageResult<AppProductSpuRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) {
PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO);
if (CollUtil.isEmpty(pageResult.getList())) {
......@@ -82,18 +64,15 @@ public class AppProductSpuController {
@GetMapping("/get-detail")
@Operation(summary = "获得商品 SPU 明细")
@Parameter(name = "id", description = "编号", required = true)
@PermitAll
public CommonResult<AppProductSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) {
// 获得商品 SPU
ProductSpuDO spu = productSpuService.getSpu(id);
ProductSpuDO spu = productSpuService.getAppSpu(id);
if (spu == null) {
throw exception(SPU_NOT_EXISTS);
}
if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) {
throw exception(SPU_NOT_ENABLE, spu.getName());
}
// 获得商品 SKU
List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());
// 增加浏览量
productSpuService.updateBrowseCount(id, 1);
......@@ -102,8 +81,7 @@ public class AppProductSpuController {
// 拼接返回
spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount());
AppProductSpuDetailRespVO spuVO = BeanUtils.toBean(spu, AppProductSpuDetailRespVO.class)
.setSkus(BeanUtils.toBean(skus, AppProductSpuDetailRespVO.Sku.class));
AppProductSpuDetailRespVO spuVO = BeanUtils.toBean(spu, AppProductSpuDetailRespVO.class);
return success(spuVO);
}
......
......@@ -18,7 +18,7 @@ public class AppProductSpuDetailRespVO {
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "商品简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是一个快乐简介")
@Schema(description = "商品规格", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是一个快乐简介")
private String introduction;
@Schema(description = "商品详情", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是商品描述")
......@@ -33,65 +33,11 @@ public class AppProductSpuDetailRespVO {
@Schema(description = "商品轮播图", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> sliderPicUrls;
// ========== 营销相关字段 =========
@Schema(description = "供应商报价")
private Integer supplierQuote;
// ========== SKU 相关字段 =========
@Schema(description = "商品单位")
private String unitName;
@Schema(description = "规格类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean specType;
@Schema(description = "商品价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer price;
@Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer marketPrice;
@Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
private Integer stock;
/**
* SKU 数组
*/
private List<Sku> skus;
// ========== 统计相关字段 =========
@Schema(description = "商品销量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer salesCount;
@Schema(description = "用户 App - 商品 SPU 明细的 SKU 信息")
@Data
public static class Sku {
@Schema(description = "商品 SKU 编号", example = "1")
private Long id;
/**
* 商品属性数组
*/
private List<AppProductPropertyValueDetailRespVO> properties;
@Schema(description = "销售价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer price;
@Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer marketPrice;
@Schema(description = "VIP 价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "968") // 通过会员等级,计算出折扣后价格
private Integer vipPrice;
@Schema(description = "图片地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
private String picUrl;
@Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer stock;
@Schema(description = "商品重量", example = "1") // 单位:kg 千克
private Double weight;
@Schema(description = "商品体积", example = "1024") // 单位:m^3 平米
private Double volume;
}
}
......@@ -15,7 +15,7 @@ public class AppProductSpuRespVO {
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "商品简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖简介")
@Schema(description = "商品规格", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖简介")
private String introduction;
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED)
......@@ -27,30 +27,10 @@ public class AppProductSpuRespVO {
@Schema(description = "商品轮播图", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> sliderPicUrls;
// ========== SKU 相关字段 =========
@Schema(description = "供应商报价")
private Integer supplierQuote;
@Schema(description = "规格类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean specType;
@Schema(description = "商品价格,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer price;
@Schema(description = "市场价,单位使用:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer marketPrice;
@Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
private Integer stock;
// ========== 营销相关字段 =========
// ========== 统计相关字段 =========
@Schema(description = "商品销量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer salesCount;
// ========== 物流相关字段 =========
@Schema(description = "配送方式数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private List<Integer> deliveryTypes;
@Schema(description = "商品单位")
private String unitName;
}
......@@ -30,7 +30,6 @@ public interface ProductSpuConvert {
default ProductSpuRespVO convert(ProductSpuDO spu, List<ProductSkuDO> skus) {
ProductSpuRespVO spuVO = BeanUtils.toBean(spu, ProductSpuRespVO.class);
spuVO.setSkus(BeanUtils.toBean(skus, ProductSkuRespVO.class));
return spuVO;
}
......
......@@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
......@@ -44,7 +45,7 @@ public class ProductSpuDO extends BaseDO {
*/
private String name;
/**
* 商品简介
* 商品规格
*/
private String introduction;
/**
......@@ -79,6 +80,11 @@ public class ProductSpuDO extends BaseDO {
*/
private Integer status;
/**
* 商品单位
*/
private String unitName;
// ========== 统计相关字段 =========
/**
......@@ -115,4 +121,10 @@ public class ProductSpuDO extends BaseDO {
*/
private String auditReason;
/**
* 供应商报价
*/
@TableField(exist = false)
private Integer supplierQuote;
}
......@@ -2,11 +2,13 @@ package cn.iocoder.foodnexus.module.product.service.spu;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils;
import cn.iocoder.foodnexus.module.customerpermission.core.annotation.CustomerVisible;
import cn.iocoder.foodnexus.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
import cn.iocoder.foodnexus.module.product.controller.admin.spu.vo.ProductSpuSaveReqVO;
import cn.iocoder.foodnexus.module.product.controller.admin.spu.vo.ProductSpuUpdateStatusReqVO;
import cn.iocoder.foodnexus.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.foodnexus.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.foodnexus.module.system.annotations.AutoSetPrice;
import cn.iocoder.foodnexus.module.system.controller.admin.vo.AuditCommonReqVO;
import jakarta.validation.Valid;
import org.springframework.scheduling.annotation.Async;
......@@ -52,6 +54,8 @@ public interface ProductSpuService {
*/
ProductSpuDO getSpu(Long id);
ProductSpuDO getAppSpu(Long id);
/**
* 获得商品 SPU
*
......
......@@ -7,10 +7,10 @@ import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.module.customerpermission.core.annotation.CustomerVisible;
import cn.iocoder.foodnexus.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.foodnexus.module.erp.api.enums.ErpAuditStatus;
import cn.iocoder.foodnexus.module.product.controller.admin.category.vo.ProductCategoryListReqVO;
import cn.iocoder.foodnexus.module.product.controller.admin.spu.vo.ProductSkuSaveReqVO;
import cn.iocoder.foodnexus.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
import cn.iocoder.foodnexus.module.product.controller.admin.spu.vo.ProductSpuSaveReqVO;
import cn.iocoder.foodnexus.module.product.controller.admin.spu.vo.ProductSpuUpdateStatusReqVO;
......@@ -19,14 +19,11 @@ import cn.iocoder.foodnexus.module.product.dal.dataobject.category.ProductCatego
import cn.iocoder.foodnexus.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.foodnexus.module.product.dal.mysql.spu.ProductSpuMapper;
import cn.iocoder.foodnexus.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.foodnexus.module.product.service.brand.ProductBrandService;
import cn.iocoder.foodnexus.module.product.service.category.ProductCategoryService;
import cn.iocoder.foodnexus.module.product.service.sku.ProductSkuService;
import cn.iocoder.foodnexus.module.system.annotations.AutoSetPrice;
import cn.iocoder.foodnexus.module.system.controller.admin.vo.AuditCommonReqVO;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Maps;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
......@@ -139,7 +136,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
.set(ProductSpuDO::getAuditor, WebFrameworkUtils.getLoginUserId())
.set(ProductSpuDO::getAuditReason, CommonUtil.getEls(auditReqVO.getAuditReason(), ""))
.set(ProductSpuDO::getAuditTime, LocalDateTime.now())
.set(ProductSpuDO::getAuditStatus, auditReqVO.getAuditReason())
.set(ProductSpuDO::getAuditStatus, auditReqVO.getAuditStatus())
.eq(ProductSpuDO::getId, id));
}
......@@ -174,6 +171,13 @@ public class ProductSpuServiceImpl implements ProductSpuService {
}
@Override
@CustomerVisible
@AutoSetPrice
public ProductSpuDO getAppSpu(Long id) {
return this.getSpu(id);
}
@Override
public ProductSpuDO getSpu(Long id, boolean includeDeleted) {
if (includeDeleted) {
return productSpuMapper.selectByIdIncludeDeleted(id);
......@@ -202,6 +206,8 @@ public class ProductSpuServiceImpl implements ProductSpuService {
}
@Override
@CustomerVisible
@AutoSetPrice
public PageResult<ProductSpuDO> getSpuPage(AppProductSpuPageReqVO pageReqVO) {
// 查找时,如果查找某个分类编号,则包含它的子分类。因为顶级分类不包含商品
Set<Long> categoryIds = new HashSet<>();
......
......@@ -21,6 +21,7 @@
<module>foodnexus-module-product</module>
<module>foodnexus-module-operations</module>
<module>foodnexus-module-product-api</module>
<module>foodnexus-module-order</module>
</modules>
</project>
......@@ -31,6 +31,10 @@
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-spring-boot-starter-biz-customer-permission</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<dependency>
......
package cn.iocoder.foodnexus.module.system.annotations;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
......
package cn.iocoder.foodnexus.module.system.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author : yanghao
* create at: 2025/9/5 10:43
* @description: 自动set价格
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoSetPrice {
/**
* 对象中的商品id字段(Long类型)
* @return
*/
String productIdField() default "id";
/**
* 对象中的价格字段(int类型)
* @return
*/
String priceField() default "supplierQuote";
}
......@@ -3,7 +3,7 @@ package cn.iocoder.foodnexus.module.system.aspect;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.module.system.annotations.AppSystemAuth;
import cn.iocoder.foodnexus.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.module.system.service.user.AdminUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
......@@ -13,7 +13,9 @@ import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.module.system.enums.ErrorCodeConstants.NOT_APP_USER;
......@@ -31,6 +33,8 @@ public class AppSystemAspect {
private final AdminUserService userService;
public static List<Long> CACHE = new ArrayList<>();
// 切点:匹配所有带有 @AppSystemAuth 注解的方法
@Pointcut("@annotation(cn.iocoder.foodnexus.module.system.annotations.AppSystemAuth)")
public void appSystemAuthPointcut() {}
......@@ -39,18 +43,21 @@ public class AppSystemAspect {
@Before("appSystemAuthPointcut() && @annotation(appSystemAuth)")
public void beforeAppSystemAuth(JoinPoint joinPoint, AppSystemAuth appSystemAuth) {
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
AdminUserDO user = userService.getUser(loginUserId);
if (appSystemAuth == null) {
if (!UserSystemEnum.isAppUser(user.getUserSystem())) {
throw exception(NOT_APP_USER);
}
} else {
// 校验用户体系是否在允许范围内
if (Arrays.stream(appSystemAuth.value()).noneMatch(e -> e.getKey() == user.getUserSystem())) {
throw exception(NOT_APP_USER);
if (!CACHE.contains(loginUserId)) {
AdminUserDO user = userService.getUser(loginUserId);
if (appSystemAuth.value() == null) {
if (!UserSystemEnum.isAppUser(user.getUserSystem())) {
throw exception(NOT_APP_USER);
}
} else {
// 校验用户体系是否在允许范围内
if (Arrays.stream(appSystemAuth.value()).noneMatch(e -> e.getKey() == user.getUserSystem())) {
throw exception(NOT_APP_USER);
}
}
}
CACHE.add(loginUserId);
}
}
package cn.iocoder.foodnexus.module.system.aspect;
import cn.iocoder.foodnexus.framework.common.exception.ServiceException;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.security.core.LoginUser;
import cn.iocoder.foodnexus.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.foodnexus.module.erp.api.service.ErpCustomerApi;
import cn.iocoder.foodnexus.module.product.api.InquireCustomerApi;
import cn.iocoder.foodnexus.module.product.api.dto.CustomerVisibleProductRespDTO;
import cn.iocoder.foodnexus.module.system.annotations.AutoSetPrice;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.module.system.enums.ErrorCodeConstants.CUSTOMER_PRODUCT_CUSTOMER_ERROR;
/**
* @author : yanghao
* create at: 2025/9/5 11:20
* @description: 自动set价格
*/
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class AutoSetPriceAspect {
@Autowired
private InquireCustomerApi inquireCustomerApi;
@Autowired
private ErpCustomerApi customerApi;
// 切点:匹配所有带有 @AutoSetPrice 注解的方法
@Pointcut("@annotation(cn.iocoder.foodnexus.module.system.annotations.AutoSetPrice)")
public void autoSetPricePointcut() {}
@Around("autoSetPricePointcut() && @annotation(autoSetPrice)")
public Object fillPrice(ProceedingJoinPoint joinPoint, AutoSetPrice autoSetPrice) throws Throwable {
Object result = joinPoint.proceed();
if (result == null) return null;
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
if (CommonUtil.isEmpty(loginUser)) {
return result;
}
Long customerId = customerApi.queryCustomerIdByUserId(loginUser.getId());
CustomerVisibleProductRespDTO customerVisibleProductRespDTO = inquireCustomerApi.queryCustomerIdByCustomerId(customerId);
if (isPager(result)) {
// Pager<T>
handlePager(result, autoSetPrice, customerVisibleProductRespDTO.getItems());
} else if (result instanceof List<?> list) {
// List<T>
handleList(list, autoSetPrice, customerVisibleProductRespDTO.getItems());
} else if (isVO(result, autoSetPrice.productIdField())) {
// 单对象 T
handleObject(result, autoSetPrice, customerVisibleProductRespDTO.getItems());
} else {
// 其他类型不处理
return result;
}
return result;
}
/** 判断是否是 PageResult<T> */
private boolean isPager(Object obj) {
return obj.getClass().getSimpleName().equals("PageResult");
}
/** 判断是否是 VO (简单通过有 productIdField 和 priceField) */
private boolean isVO(Object obj, String productFile) {
try {
obj.getClass().getDeclaredField(productFile);
return true;
} catch (NoSuchFieldException e) {
return false;
}
}
private void handlePager(Object pagerObj, AutoSetPrice autoSetPrice, List<CustomerVisibleProductRespDTO.CustomerProduct> priceDataList) {
try {
if (pagerObj instanceof PageResult) {
PageResult pageResult = (PageResult) pagerObj;
handleList(pageResult.getList(), autoSetPrice, priceDataList);
}
} catch (Exception e) {
log.error("error:", e);
throw exception(CUSTOMER_PRODUCT_CUSTOMER_ERROR);
}
}
private void handleList(List<?> list, AutoSetPrice autoSetPrice, List<CustomerVisibleProductRespDTO.CustomerProduct> priceDataList) {
List<Long> ids = new ArrayList<>();
for (Object obj : list) {
Long id = getProductId(obj, autoSetPrice);
if (id != null) ids.add(id);
}
if (ids.isEmpty()) return;
for (Object obj : list) {
Long id = getProductId(obj, autoSetPrice);
if (id != null) {
Integer supplierQuote = priceDataList.stream().filter(item -> item.getProductId().equals(id)).findFirst()
.orElseThrow(() -> new ServiceException(CUSTOMER_PRODUCT_CUSTOMER_ERROR))
.getSupplierQuote();
setPrice(obj, autoSetPrice, supplierQuote);
}
}
}
private void handleObject(Object obj, AutoSetPrice autoSetPrice, List<CustomerVisibleProductRespDTO.CustomerProduct> priceDataList) {
Long id = getProductId(obj, autoSetPrice);
if (id == null) return;
Integer supplierQuote = priceDataList.stream().filter(item -> item.getProductId().equals(id)).findFirst()
.orElseThrow(() -> new ServiceException(CUSTOMER_PRODUCT_CUSTOMER_ERROR))
.getSupplierQuote();
setPrice(obj, autoSetPrice, supplierQuote);
}
/** 获取 productId,并做类型校验 */
private Long getProductId(Object obj, AutoSetPrice autoSetPrice) {
try {
Field field = obj.getClass().getDeclaredField(autoSetPrice.productIdField());
field.setAccessible(true);
if (!field.getType().equals(Long.class)) {
log.error(String.format("字段 %s 必须是 Long 类型,实际是 %s",
autoSetPrice.productIdField(), field.getType().getSimpleName()));
throw exception(
String.format("字段 %s 必须是 Long 类型,实际是 %s",
autoSetPrice.productIdField(), field.getType().getSimpleName())
);
}
return (Long) field.get(obj);
} catch (NoSuchFieldException e) {
log.error("未找到字段: " + autoSetPrice.productIdField(), e);
throw exception("未找到字段: " + autoSetPrice.productIdField());
} catch (Exception e) {
throw exception(CUSTOMER_PRODUCT_CUSTOMER_ERROR);
}
}
/** 设置 price,并做类型校验 */
private void setPrice(Object obj, AutoSetPrice autoSetPrice, Integer value) {
try {
Field field = obj.getClass().getDeclaredField(autoSetPrice.priceField());
field.setAccessible(true);
if (!field.getType().equals(Integer.class) && !field.getType().equals(int.class)) {
log.error(String.format("字段 %s 必须是 int/Integer 类型,实际是 %s",
autoSetPrice.priceField(), field.getType().getSimpleName()));
throw exception(
String.format("字段 %s 必须是 int/Integer 类型,实际是 %s",
autoSetPrice.priceField(), field.getType().getSimpleName())
);
}
field.set(obj, value);
} catch (NoSuchFieldException e) {
log.error("未找到字段: " + autoSetPrice.priceField(), e);
throw exception("未找到字段: " + autoSetPrice.priceField());
} catch (Exception e) {
throw exception(CUSTOMER_PRODUCT_CUSTOMER_ERROR);
}
}
}
......@@ -4,8 +4,7 @@ import cn.iocoder.foodnexus.framework.common.enums.CommonStatusEnum;
import cn.iocoder.foodnexus.framework.common.validation.InEnum;
import cn.iocoder.foodnexus.module.erp.api.vo.customer.CustomerAddReqVO;
import cn.iocoder.foodnexus.module.erp.api.vo.supplier.SupplierAddReqVO;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import com.mzt.logapi.starter.annotation.DiffLogField;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
......
......@@ -3,7 +3,7 @@ package cn.iocoder.foodnexus.module.system.controller.admin.user.vo.user;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.foodnexus.framework.common.validation.InEnum;
import cn.iocoder.foodnexus.framework.common.validation.Mobile;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.module.system.framework.operatelog.core.DeptParseFunction;
import cn.iocoder.foodnexus.module.system.framework.operatelog.core.PostParseFunction;
import cn.iocoder.foodnexus.module.system.framework.operatelog.core.SexParseFunction;
......
......@@ -3,7 +3,7 @@ package cn.iocoder.foodnexus.module.system.dal.dataobject.dept;
import cn.iocoder.foodnexus.framework.common.enums.CommonStatusEnum;
import cn.iocoder.foodnexus.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.foodnexus.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
......
......@@ -2,7 +2,7 @@ package cn.iocoder.foodnexus.module.system.dal.dataobject.user;
import cn.iocoder.foodnexus.framework.common.enums.CommonStatusEnum;
import cn.iocoder.foodnexus.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.module.system.enums.common.SexEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
......
......@@ -107,4 +107,11 @@ public interface RedisKeyConstants {
*/
String WXA_SUBSCRIBE_TEMPLATE = "wxa_subscribe_template";
/**
* 客户可见商品的缓存
* KEY:{customerId}
* VALUE 客户id
*/
String CUSTOMER_VISIBLE_PRODUCT = "customer_visible_product";
}
......@@ -171,4 +171,8 @@ public interface ErrorCodeConstants {
// ========== 站内信发送 1-002-028-000 ==========
ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失");
// ========== 商品价格 1-002-029-000 ==========
ErrorCode CUSTOMER_PRODUCT_CUSTOMER_ERROR = new ErrorCode(1_002_029_000, "商品获取价格异常");
}
......@@ -15,7 +15,7 @@ import cn.iocoder.foodnexus.module.system.controller.admin.auth.vo.*;
import cn.iocoder.foodnexus.module.system.convert.auth.AuthConvert;
import cn.iocoder.foodnexus.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import cn.iocoder.foodnexus.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import cn.iocoder.foodnexus.module.system.enums.logger.LoginLogTypeEnum;
import cn.iocoder.foodnexus.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.foodnexus.module.system.enums.oauth2.OAuth2ClientConstants;
......@@ -33,7 +33,6 @@ import jakarta.annotation.Resource;
import jakarta.validation.Validator;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.xmlbeans.UserType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......
......@@ -3,7 +3,6 @@ package cn.iocoder.foodnexus.module.system.service.dept;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.foodnexus.framework.common.enums.CommonStatusEnum;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.foodnexus.module.erp.api.service.ErpCustomerApi;
......@@ -13,7 +12,7 @@ import cn.iocoder.foodnexus.module.system.controller.admin.dept.vo.dept.DeptSave
import cn.iocoder.foodnexus.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.foodnexus.module.system.dal.mysql.dept.DeptMapper;
import cn.iocoder.foodnexus.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.foodnexus.module.system.enums.UserSystemEnum;
import cn.iocoder.foodnexus.framework.common.enums.UserSystemEnum;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
......
......@@ -197,15 +197,17 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
* @return 用户信息
*/
private Map<String, String> buildUserInfo(Long userId, Integer userType) {
if (userType.equals(UserTypeEnum.ADMIN.getValue())) {
/*if (userType.equals(UserTypeEnum.ADMIN.getValue())) {*/
AdminUserDO user = adminUserService.getUser(userId);
return MapUtil.builder(LoginUser.INFO_KEY_NICKNAME, user.getNickname())
.put(LoginUser.INFO_KEY_USER_SYSTEM, user.getUserSystem())
.put(LoginUser.INFO_KEY_DEPT_ID, StrUtil.toStringOrNull(user.getDeptId())).build();
} else if (userType.equals(UserTypeEnum.MEMBER.getValue())) {
/*} else if (userType.equals(UserTypeEnum.MEMBER.getValue())) {
// 注意:目前 Member 暂时不读取,可以按需实现
return Collections.emptyMap();
}
return null;
*/
}
private static String generateAccessToken() {
......
......@@ -81,6 +81,11 @@
<artifactId>foodnexus-module-operations</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-module-order</artifactId>
<version>${revision}</version>
</dependency>
<!--<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>foodnexus-module-trade</artifactId>
......
......@@ -27,7 +27,7 @@ spring:
cache:
type: REDIS
redis:
time-to-live: 1h # 设置过期时间为 1 小时
time-to-live: 12h # 设置过期时间为 12 小时
server:
servlet:
......@@ -69,7 +69,7 @@ mybatis-plus:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
id-type: ASSIGN_ID # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
# id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
# id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
# id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
......@@ -81,7 +81,7 @@ mybatis-plus:
password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成
mybatis-plus-join:
banner: false # 是否打印 mybatis plus join banner,默认true
banner: true # 是否打印 mybatis plus join banner,默认true
sub-table-logic: true # 全局启用副表逻辑删除,默认true。关闭后关联查询不会加副表逻辑删除
ms-cache: true # 拦截器MappedStatement缓存,默认 true
table-alias: t # 表别名(默认 t)
......
......@@ -24,6 +24,7 @@
<!-- <module>foodnexus-module-crm</module>-->
<module>foodnexus-module-erp</module>
<module>foodnexus-module-erp-api</module>
<module>foodnexus-framework/foodnexus-spring-boot-starter-biz-customer-permission</module>
<!-- <module>foodnexus-module-ai</module>-->
<!--<module>foodnexus-module-iot</module>-->
</modules>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment