feat: 增加上期结余

This commit is contained in:
2024-11-17 19:52:51 +08:00
parent 94d4dfebcb
commit 04ba884d46
16 changed files with 363 additions and 56 deletions

View File

@@ -89,6 +89,13 @@
<artifactId>poi-ooxml</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.22.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>

View File

@@ -685,6 +685,24 @@ public class CommonQuery {
return conversionService.convert(queryItem.value, targetType);
}
public QueryItem deleteItem(String key) {
if (CollectionUtils.isEmpty(items)) {
return null;
}
QueryItem queryItem = items.stream().filter(x -> x.key.equals(key)).findFirst().orElse(null);
if (queryItem != null) {
items.remove(queryItem);
}
return queryItem;
}
public static class RandomNameGenerator {
private final Set<String> names = new HashSet<>();
@@ -724,15 +742,18 @@ public class CommonQuery {
@Data
@With
@AllArgsConstructor
@NoArgsConstructor
public static class QueryItem {
private Boolean not;
private String key;
private String opt;
private String value;
@Builder.Default private String group = "default";
private String group = "default";
private String min;
private String max;
private String type;
private Boolean includeNull;
}
}

View File

@@ -0,0 +1,61 @@
package cn.lihongjie.coal.common;
import lombok.experimental.UtilityClass;
import java.util.*;
@UtilityClass
public class ThreadLocalUtils {
private static final ThreadLocal<Map<String, Object>> threadLocal =
ThreadLocal.withInitial(HashMap::new);
public static void set(String key, Object value) {
threadLocal.get().put(key, value);
}
public static Object get(String key) {
return get(key, null);
}
public static Object get(String key, Object defaultValue) {
return threadLocal.get().getOrDefault(key, defaultValue);
}
public static void remove(String key) {
threadLocal.get().remove(key);
}
public static void clear() {
threadLocal.remove();
}
public static void runWith(String key, Object value, Runnable runnable) {
Object oldVal = get(key, null);
set(key, value);
try {
runnable.run();
} finally {
if (oldVal == null) {
remove(key);
} else {
set(key, oldVal);
}
}
}
public static void runWith(Map<String, Object> map, Runnable runnable) {
Map<String, Object> prev = threadLocal.get();
HashMap<String, Object> tmp = new HashMap<>(prev);
tmp.putAll(map);
threadLocal.set(tmp);
try {
runnable.run();
} finally {
threadLocal.set(prev);
}
}
}

View File

@@ -16,8 +16,8 @@ public class CreateOrganizationDto extends CommonDto {
private String orgAdminUserName;
private String orgAdminPassword;
private String orgAdminName;
private String orgAdminPhone;
@Comment("最大用户数量")
private Integer maxUser;

View File

@@ -117,6 +117,8 @@ public class OrganizationService extends BaseService<OrganizationEntity, Organiz
dto.setOrganizationId(entity.getId());
dto.setUsername(request.getOrgAdminUserName());
dto.setPassword(request.getOrgAdminPassword());
dto.setName(request.getOrgAdminName());
dto.setPhone(request.getOrgAdminPhone());
UserDto orgAdmin = userService.createOrgAdmin(dto);
entity.setAdminUser(em.getReference(UserEntity.class, orgAdmin.getId()));
@@ -231,4 +233,7 @@ public class OrganizationService extends BaseService<OrganizationEntity, Organiz
return new HashSet<>(
id);
}
}

View File

@@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.Flyway;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
@@ -55,6 +56,8 @@ public class InitDataRunner implements CommandLineRunner {
@Autowired ErrorMsgService errorMsgService;
@Autowired SchedulerFactoryBean schedulerFactoryBean;
@Override
@Transactional
// @Async
@@ -116,5 +119,7 @@ public class InitDataRunner implements CommandLineRunner {
}
organizationService.init();
schedulerFactoryBean.start();
}
}

View File

@@ -7,23 +7,32 @@ import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import java.util.List;
import java.util.Map;
@Configuration
public class HibernateConfig {
@Autowired MyRedissonRegionFactory myRedissonRegionFactory;
@Autowired Environment environment;
@Autowired
private List<StatementInspector> statementInspectors;
@Bean
HibernatePropertiesCustomizer hibernatePropertiesCustomizer() {
@@ -32,10 +41,25 @@ public class HibernateConfig {
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put(
AvailableSettings.DIALECT, MyPostgreSQLDialect.class.getCanonicalName());
AnnotationAwareOrderComparator.sort(statementInspectors);
hibernateProperties.put(AvailableSettings.STATEMENT_INSPECTOR, new StatementInspector() {
@Override
public String inspect(String sql) {
for (StatementInspector statementInspector : statementInspectors) {
sql = statementInspector.inspect(sql);
}
return sql;
}
});
// <property name="hibernate.cache.region.factory_class"
// value="org.redisson.hibernate.RedissonRegionFactory" />
hibernateProperties.put(
"hibernate.cache.region.factory_class", myRedissonRegionFactory);
// hibernateProperties.put(
// "hibernate.cache.region.factory_class", myRedissonRegionFactory);
Map<String, Object> propertiesStartingWith =
SpringUtils.getPropertiesStartingWith(

View File

@@ -4,7 +4,6 @@ import com.p6spy.engine.spy.P6DataSource;
import lombok.SneakyThrows;
import org.flywaydb.core.Flyway;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.context.annotation.Bean;
@@ -18,17 +17,16 @@ import javax.sql.DataSource;
@Configuration
public class QuartzConfig {
@Autowired Flyway flyway;
@Autowired private DataSource dataSource;
@Bean
public SchedulerFactoryBeanCustomizer schedulerFactoryBeanCustomizer() {
flyway.migrate();
return new SchedulerFactoryBeanCustomizer() {
@SneakyThrows
@Override
public void customize(SchedulerFactoryBean schedulerFactoryBean) {
schedulerFactoryBean.setAutoStartup(false);
if (dataSource instanceof P6DataSource p6DataSource) {
// schedulerFactoryBean.setDataSource(dataSource);

View File

@@ -0,0 +1,35 @@
package cn.lihongjie.coal.warehouseGoodsSummary;
import cn.lihongjie.coal.common.ThreadLocalUtils;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(-1)
public class TimeStatementInspector implements StatementInspector {
@Override
public String inspect(String sql) {
if (!(Boolean) ThreadLocalUtils.get("enableTimeStatementInspector", false)) {
return sql;
}
String startTime = (String) ThreadLocalUtils.get("startTime", null);
String endTime = (String) ThreadLocalUtils.get("endTime", null);
if (startTime != null) {
sql = sql.replace(":startTime", startTime);
}
if (endTime != null) {
sql = sql.replace(":endTime", endTime);
}
return sql;
}
}

View File

@@ -12,23 +12,13 @@ import org.hibernate.annotations.Comment;
@Data
public class WarehouseGoodsSummaryDto extends OrgCommonDto {
@ManyToOne private WarehouseDto category;
@ManyToOne
private WarehouseDto category;
@ManyToOne
private WarehouseDto brand;
@ManyToOne
private WarehouseDto unit;
@ManyToOne
private WarehouseDto warehouse;
@ManyToOne private WarehouseDto brand;
@ManyToOne private WarehouseDto unit;
@ManyToOne private WarehouseDto warehouse;
private Integer detailCount;
@@ -36,10 +26,11 @@ public class WarehouseGoodsSummaryDto extends OrgCommonDto {
private Double type1Number;
private Double type0Amount;
private Double prevNumber;
private Double prevAmount;
private Double type1Amount;
@Comment("条码")
private String barCode;
@@ -47,7 +38,6 @@ public class WarehouseGoodsSummaryDto extends OrgCommonDto {
private String spec;
@Comment("型号")
private String model;
@Comment("重量 kg")
@@ -61,22 +51,21 @@ public class WarehouseGoodsSummaryDto extends OrgCommonDto {
@Comment("进货价")
private Double price1;
@Comment("市场价")
private Double price2;
@Comment("仓库核算价")
private Double price3;
@Comment("批发价")
private Double price4;
private Double price5;
@Comment("是否启用保质期")
private Boolean enableShelfLife;
@Comment("保质期 天")
private Integer shelfLife;
}

View File

@@ -11,8 +11,7 @@ import jakarta.persistence.ManyToOne;
import lombok.Data;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.Subselect;
import org.hibernate.annotations.*;
@Data
@Entity
@@ -51,14 +50,14 @@ select
g.shelf_life,
d.warehouse_id as warehouse_id,
count(d.id) over (partition by g.id) as detail_count,
round((sum(d.number) filter ( where d.receipt_type = '0' ) over (partition by g.id, d.warehouse_id))::numeric,
round((sum(d.number) filter ( where d.receipt_type = '0' and (r.receipt_date >= :startTime and r.receipt_date <= :endTime) ) over (partition by g.id, d.warehouse_id))::numeric,
2) as type0number,
round((sum(d.number) filter ( where d.receipt_type = '1' ) over (partition by g.id, d.warehouse_id))::numeric,
round((sum(d.number) filter ( where d.receipt_type = '1' and (r.receipt_date >= :startTime and r.receipt_date <= :endTime) ) over (partition by g.id, d.warehouse_id))::numeric,
2) as type1number,
round((sum(d.amount) filter ( where d.receipt_type = '0' ) over (partition by g.id, d.warehouse_id))::numeric,
round((sum(d.amount) filter ( where d.receipt_type = '0' and (r.receipt_date >= :startTime and r.receipt_date <= :endTime) ) over (partition by g.id, d.warehouse_id))::numeric,
2) as type0amount,
round((sum(d.amount) filter ( where d.receipt_type = '1' ) over (partition by g.id, d.warehouse_id))::numeric,
round((sum(d.amount) filter ( where d.receipt_type = '1' and (r.receipt_date >= :startTime and r.receipt_date <= :endTime) ) over (partition by g.id, d.warehouse_id))::numeric,
2) as type1amount
from t_warehouse_goods g
@@ -67,6 +66,12 @@ from t_warehouse_goods g
""")
@FilterDef(name = "warehouseGoodsSummaryTimeFilter", parameters = {
@ParamDef(name = "startTime", type = java.time.LocalDateTime.class),
@ParamDef(name = "endTime", type = java.time.LocalDateTime.class),
})
public class WarehouseGoodsSummaryEntity extends OrgCommonEntity {
@ManyToOne

View File

@@ -1,8 +1,12 @@
package cn.lihongjie.coal.warehouseGoodsSummary.service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.base.mapper.CommonMapper;
import cn.lihongjie.coal.base.service.BaseService;
import cn.lihongjie.coal.common.ThreadLocalUtils;
import cn.lihongjie.coal.dbFunctions.DbFunctionService;
import cn.lihongjie.coal.exception.BizException;
import cn.lihongjie.coal.warehouseGoodsSummary.dto.CreateWarehouseGoodsSummaryDto;
@@ -12,12 +16,16 @@ import cn.lihongjie.coal.warehouseGoodsSummary.entity.WarehouseGoodsSummaryEntit
import cn.lihongjie.coal.warehouseGoodsSummary.mapper.WarehouseGoodsSummaryMapper;
import cn.lihongjie.coal.warehouseGoodsSummary.repository.WarehouseGoodsSummaryRepository;
import io.vavr.Tuple2;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.criteria.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
@@ -27,6 +35,9 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -35,6 +46,9 @@ import java.util.List;
@Transactional
public class WarehouseGoodsSummaryService
extends BaseService<WarehouseGoodsSummaryEntity, WarehouseGoodsSummaryRepository> {
public static final LocalDateTime MIN_TIME = LocalDateTime.of(1970, 1, 1, 0, 0, 0);
public static final LocalDateTime MAX_TIME = LocalDateTime.of(2050, 1, 1, 0, 0, 0);
@PersistenceContext EntityManager em;
@Autowired private WarehouseGoodsSummaryRepository repository;
@Autowired private WarehouseGoodsSummaryMapper mapper;
@@ -74,8 +88,16 @@ public class WarehouseGoodsSummaryService
return mapper.toDto(entity);
}
@Autowired
private CommonMapper commonMapper;
public Page<WarehouseGoodsSummaryDto> list(CommonQuery query) {
CommonQuery copy = new CommonQuery();
BeanUtil.copyProperties(query, copy);
setFilter(query);
Page<WarehouseGoodsSummaryEntity> page =
repository.findAll(
query.specification(conversionService),
@@ -84,43 +106,123 @@ public class WarehouseGoodsSummaryService
query.getPageSize(),
Sort.by(query.getOrders())));
return page.map(this.mapper::toDto);
ThreadLocalUtils.remove("startTime");
ThreadLocalUtils.remove("endTime");
ThreadLocalUtils.remove("enableTimeStatementInspector");
Page<WarehouseGoodsSummaryDto> dtos = page.map(this.mapper::toDto);
if (CollectionUtils.isEmpty(copy.getItems())){
copy.setItems(new ArrayList<>());
}
CommonQuery.QueryItem startTimeItem = copy.deleteItem("startTime");
CommonQuery.QueryItem endTimeItem = copy.deleteItem("endTime");
if (startTimeItem == null || conversionService.convert(startTimeItem.getValue(), LocalDateTime.class).equals(MIN_TIME)) {
return dtos;
}
LocalDateTime startTime = MIN_TIME;
LocalDateTime endTime =
conversionService.convert(startTimeItem.getValue(), LocalDateTime.class).minusSeconds(1);
copy.getItems().add(new CommonQuery.QueryItem().withKey("startTime").withValue(LocalDateTimeUtil.format(startTime, "yyyy-MM-dd HH:mm:ss")));
copy.getItems().add(new CommonQuery.QueryItem().withKey("endTime").withValue(LocalDateTimeUtil.format(endTime, "yyyy-MM-dd HH:mm:ss")));
Page<WarehouseGoodsSummaryDto> prevSummary = list(copy);
List<Tuple2<WarehouseGoodsSummaryDto, WarehouseGoodsSummaryDto>> join = cn.lihongjie.coal.common.CollectionUtils.leftHashJoin(
dtos.getContent(),
prevSummary.getContent(),
WarehouseGoodsSummaryDto::getId,
WarehouseGoodsSummaryDto::getId
);
for (Tuple2<WarehouseGoodsSummaryDto, WarehouseGoodsSummaryDto> tuple2 : join) {
WarehouseGoodsSummaryDto dto = tuple2._1;
WarehouseGoodsSummaryDto entity = tuple2._2;
dto.setPrevAmount(ObjectUtils.defaultIfNull(entity.getType0Amount(), 0.0) - ObjectUtils.defaultIfNull(entity.getType1Amount(), 0.0));
dto.setPrevNumber(ObjectUtils.defaultIfNull(entity.getType0Number(), 0.0) - ObjectUtils.defaultIfNull(entity.getType1Number(), 0.0));
}
return dtos;
}
@PersistenceContext EntityManager em;
private void setFilter(CommonQuery query) {
CommonQuery.QueryItem startTimeItem = query.deleteItem("startTime");
CommonQuery.QueryItem endTimeItem = query.deleteItem("endTime");
LocalDateTime startTime =
startTimeItem == null
? MIN_TIME
: conversionService.convert(startTimeItem.getValue(), LocalDateTime.class);
LocalDateTime endTime =
endTimeItem == null
? MAX_TIME
: conversionService.convert(endTimeItem.getValue(), LocalDateTime.class);
ThreadLocalUtils.set(
"startTime",
"'%s'::timestamp"
.formatted(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.format(startTime)));
ThreadLocalUtils.set(
"endTime",
"'%s'::timestamp"
.formatted(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.format(endTime)));
ThreadLocalUtils.set("enableTimeStatementInspector", true);
}
public WarehouseGoodsSummaryDto getSummary(CommonQuery query) {
setFilter(query);
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<WarehouseGoodsSummaryEntity> criteriaQuery = builder.createQuery(WarehouseGoodsSummaryEntity.class);
CriteriaQuery<WarehouseGoodsSummaryEntity> criteriaQuery =
builder.createQuery(WarehouseGoodsSummaryEntity.class);
Root<WarehouseGoodsSummaryEntity> root = criteriaQuery.from(WarehouseGoodsSummaryEntity.class);
Root<WarehouseGoodsSummaryEntity> root =
criteriaQuery.from(WarehouseGoodsSummaryEntity.class);
criteriaQuery = criteriaQuery.multiselect(
Arrays.asList(
builder.sumAsDouble(root.get("type0Number")).alias("type0Number"),
builder.sumAsDouble(root.get("type1Number")).alias("type1Number"),
builder.sumAsDouble(root.get("type0Amount")).alias("type0Amount"),
builder.sumAsDouble(root.get("type1Amount")).alias("type1Amount")));
criteriaQuery =
criteriaQuery.multiselect(
Arrays.asList(
builder.sumAsDouble(root.get("type0Number")).alias("type0Number"),
builder.sumAsDouble(root.get("type1Number")).alias("type1Number"),
builder.sumAsDouble(root.get("type0Amount")).alias("type0Amount"),
builder.sumAsDouble(root.get("type1Amount")).alias("type1Amount")));
Specification specification = query.specification(conversionService);
Predicate predicate = specification.toPredicate(root, criteriaQuery, builder);
criteriaQuery = criteriaQuery.where(predicate);
List<WarehouseGoodsSummaryEntity> resultList =
em.createQuery(criteriaQuery).getResultList();
List<WarehouseGoodsSummaryEntity> resultList = em.createQuery(criteriaQuery).getResultList();
ThreadLocalUtils.remove("startTime");
ThreadLocalUtils.remove("endTime");
ThreadLocalUtils.remove("enableTimeStatementInspector");
return mapper.toDto(resultList.get(0));
}
}

View File

@@ -0,0 +1,4 @@
spring:
datasource:
druid:
url:

View File

@@ -12,5 +12,5 @@ def service = ioc.getBean(DictionaryService.class)
return service.autoComplete(new AutoCompleteRequest(tableName: "t_coal_washing_daily_analysis", fieldName: "name", organizationId: Ctx.currentUser().organizationId,query: params.get("query")?.asText()))
return service.autoComplete(new AutoCompleteRequest(tableName: "t_coal_washing_daily_analysis", fieldName: "name", organizationId: Ctx.currentUser().organizationId,query: params.get("query", null)?.asText()))

View File

@@ -12,5 +12,5 @@ def service = ioc.getBean(DictionaryService.class)
return service.autoComplete(new AutoCompleteRequest(tableName: "t_coal_washing_daily_analysis_kf_items kf inner join t_coal_washing_daily_analysis a on kf.coal_washing_daily_analysis_id = a.id", fieldName: "kf.name",organizationId: Ctx.currentUser().organizationId, query: params.get("query")?.asText()))
return service.autoComplete(new AutoCompleteRequest(tableName: "t_coal_washing_daily_analysis_kf_items kf inner join t_coal_washing_daily_analysis a on kf.coal_washing_daily_analysis_id = a.id", fieldName: "kf.name",organizationId: Ctx.currentUser().organizationId, query: params.get("query", null)?.asText()))

View File

@@ -0,0 +1,51 @@
package cn.lihongjie.coal.warehouseGoodsSummary.service;
import static org.junit.jupiter.api.Assertions.*;
import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.runner.InitDataRunner;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import java.util.*;
@SpringBootTest(
properties = {
"PG_DATABASE=coal_master",
"spring.profiles.active=master",
"spring.jpa.hibernate.ddl-auto=none",
"spring.main.lazy-initialization=true"
}
)
@MockBean(InitDataRunner.class)
class WarehouseGoodsSummaryServiceTest {
@Autowired
WarehouseGoodsSummaryService service;
@Test
@DisplayName("验证sql拦截器是否生效")
void test1() {
service.list(new CommonQuery());
}
@Test
@DisplayName("验证上期结余是否正确")
void test2() {
CommonQuery query = new CommonQuery();
query.setItems(new ArrayList<>());
query.getItems().add(new CommonQuery.QueryItem().withKey("startTime").withOpt("ge").withValue("2021-01-01 00:00:00"));
service.list(query);
}
}