diff --git a/fund-common/pom.xml b/fund-common/pom.xml index 4eb3e7b..f12dd62 100644 --- a/fund-common/pom.xml +++ b/fund-common/pom.xml @@ -37,6 +37,18 @@ org.slf4j slf4j-api + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + com.fasterxml.jackson.core + jackson-databind + diff --git a/fund-common/src/main/java/com/fundplatform/common/cache/RedisService.java b/fund-common/src/main/java/com/fundplatform/common/cache/RedisService.java new file mode 100644 index 0000000..6d1243c --- /dev/null +++ b/fund-common/src/main/java/com/fundplatform/common/cache/RedisService.java @@ -0,0 +1,276 @@ +package com.fundplatform.common.cache; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Redis缓存服务 + */ +@Service +public class RedisService { + + private final RedisTemplate redisTemplate; + + public RedisService(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + // ==================== String操作 ==================== + + /** + * 设置缓存 + */ + public void set(String key, Object value) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 设置缓存并设置过期时间 + */ + public void set(String key, Object value, long timeout, TimeUnit unit) { + redisTemplate.opsForValue().set(key, value, timeout, unit); + } + + /** + * 设置缓存并设置过期时间(秒) + */ + public void setEx(String key, Object value, long seconds) { + redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS); + } + + /** + * 获取缓存 + */ + public Object get(String key) { + return redisTemplate.opsForValue().get(key); + } + + /** + * 获取缓存并转换为指定类型 + */ + @SuppressWarnings("unchecked") + public T get(String key, Class clazz) { + Object value = redisTemplate.opsForValue().get(key); + if (value == null) { + return null; + } + return (T) value; + } + + /** + * 删除缓存 + */ + public Boolean delete(String key) { + return redisTemplate.delete(key); + } + + /** + * 批量删除缓存 + */ + public Long delete(Collection keys) { + return redisTemplate.delete(keys); + } + + /** + * 判断key是否存在 + */ + public Boolean hasKey(String key) { + return redisTemplate.hasKey(key); + } + + /** + * 设置过期时间 + */ + public Boolean expire(String key, long timeout, TimeUnit unit) { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取过期时间 + */ + public Long getExpire(String key) { + return redisTemplate.getExpire(key); + } + + /** + * 自增 + */ + public Long increment(String key) { + return redisTemplate.opsForValue().increment(key); + } + + /** + * 自增指定值 + */ + public Long increment(String key, long delta) { + return redisTemplate.opsForValue().increment(key, delta); + } + + /** + * 自减 + */ + public Long decrement(String key) { + return redisTemplate.opsForValue().decrement(key); + } + + // ==================== Hash操作 ==================== + + /** + * 设置Hash字段 + */ + public void hSet(String key, String field, Object value) { + redisTemplate.opsForHash().put(key, field, value); + } + + /** + * 设置Hash多个字段 + */ + public void hSetAll(String key, Map map) { + redisTemplate.opsForHash().putAll(key, map); + } + + /** + * 获取Hash字段 + */ + public Object hGet(String key, String field) { + return redisTemplate.opsForHash().get(key, field); + } + + /** + * 获取Hash所有字段 + */ + public Map hGetAll(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 删除Hash字段 + */ + public Long hDelete(String key, Object... fields) { + return redisTemplate.opsForHash().delete(key, fields); + } + + /** + * 判断Hash字段是否存在 + */ + public Boolean hHasKey(String key, String field) { + return redisTemplate.opsForHash().hasKey(key, field); + } + + // ==================== List操作 ==================== + + /** + * 左推入List + */ + public Long lPush(String key, Object value) { + return redisTemplate.opsForList().leftPush(key, value); + } + + /** + * 右推入List + */ + public Long rPush(String key, Object value) { + return redisTemplate.opsForList().rightPush(key, value); + } + + /** + * 左弹出List + */ + public Object lPop(String key) { + return redisTemplate.opsForList().leftPop(key); + } + + /** + * 右弹出List + */ + public Object rPop(String key) { + return redisTemplate.opsForList().rightPop(key); + } + + /** + * 获取List范围 + */ + public List lRange(String key, long start, long end) { + return redisTemplate.opsForList().range(key, start, end); + } + + /** + * 获取List长度 + */ + public Long lSize(String key) { + return redisTemplate.opsForList().size(key); + } + + // ==================== Set操作 ==================== + + /** + * 添加Set成员 + */ + public Long sAdd(String key, Object... values) { + return redisTemplate.opsForSet().add(key, values); + } + + /** + * 移除Set成员 + */ + public Long sRemove(String key, Object... values) { + return redisTemplate.opsForSet().remove(key, values); + } + + /** + * 获取Set所有成员 + */ + public Set sMembers(String key) { + return redisTemplate.opsForSet().members(key); + } + + /** + * 判断是否为Set成员 + */ + public Boolean sIsMember(String key, Object value) { + return redisTemplate.opsForSet().isMember(key, value); + } + + /** + * 获取Set大小 + */ + public Long sSize(String key) { + return redisTemplate.opsForSet().size(key); + } + + // ==================== ZSet操作 ==================== + + /** + * 添加ZSet成员 + */ + public Boolean zAdd(String key, Object value, double score) { + return redisTemplate.opsForZSet().add(key, value, score); + } + + /** + * 移除ZSet成员 + */ + public Long zRemove(String key, Object... values) { + return redisTemplate.opsForZSet().remove(key, values); + } + + /** + * 获取ZSet范围 + */ + public Set zRange(String key, long start, long end) { + return redisTemplate.opsForZSet().range(key, start, end); + } + + /** + * 获取ZSet大小 + */ + public Long zSize(String key) { + return redisTemplate.opsForZSet().size(key); + } +} diff --git a/fund-common/src/main/java/com/fundplatform/common/config/RedisConfig.java b/fund-common/src/main/java/com/fundplatform/common/config/RedisConfig.java new file mode 100644 index 0000000..897e3ab --- /dev/null +++ b/fund-common/src/main/java/com/fundplatform/common/config/RedisConfig.java @@ -0,0 +1,48 @@ +package com.fundplatform.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; + +/** + * Redis配置类 + */ +@Configuration +public class RedisConfig { + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值 + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); + jackson2JsonRedisSerializer.setObjectMapper(mapper); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + + // key采用String的序列化方式 + template.setKeySerializer(stringRedisSerializer); + // hash的key也采用String的序列化方式 + template.setHashKeySerializer(stringRedisSerializer); + // value序列化方式采用jackson + template.setValueSerializer(jackson2JsonRedisSerializer); + // hash的value序列化方式采用jackson + template.setHashValueSerializer(jackson2JsonRedisSerializer); + + template.afterPropertiesSet(); + return template; + } +} diff --git a/fund-cust/pom.xml b/fund-cust/pom.xml index 8fe8ec8..44dcb82 100644 --- a/fund-cust/pom.xml +++ b/fund-cust/pom.xml @@ -40,6 +40,11 @@ spring-cloud-starter-openfeign 4.0.0 + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + diff --git a/fund-cust/src/main/java/com/fundplatform/cust/CustApplication.java b/fund-cust/src/main/java/com/fundplatform/cust/CustApplication.java index 7c20e8f..0c0e49d 100644 --- a/fund-cust/src/main/java/com/fundplatform/cust/CustApplication.java +++ b/fund-cust/src/main/java/com/fundplatform/cust/CustApplication.java @@ -2,9 +2,11 @@ package com.fundplatform.cust; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication(scanBasePackages = {"com.fundplatform.cust", "com.fundplatform.common"}) +@EnableDiscoveryClient @EnableFeignClients public class CustApplication { diff --git a/fund-cust/src/main/resources/application.yml b/fund-cust/src/main/resources/application.yml index f36dcf2..ddd3b3e 100644 --- a/fund-cust/src/main/resources/application.yml +++ b/fund-cust/src/main/resources/application.yml @@ -5,6 +5,13 @@ spring: application: name: fund-cust + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: fund-platform + group: DEFAULT_GROUP + datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/fund_cust?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai diff --git a/fund-exp/pom.xml b/fund-exp/pom.xml index eb5e670..05f6e15 100644 --- a/fund-exp/pom.xml +++ b/fund-exp/pom.xml @@ -33,6 +33,11 @@ com.mysql mysql-connector-j + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + diff --git a/fund-exp/src/main/java/com/fundplatform/exp/ExpApplication.java b/fund-exp/src/main/java/com/fundplatform/exp/ExpApplication.java index a72852e..5a4080a 100644 --- a/fund-exp/src/main/java/com/fundplatform/exp/ExpApplication.java +++ b/fund-exp/src/main/java/com/fundplatform/exp/ExpApplication.java @@ -2,8 +2,10 @@ package com.fundplatform.exp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication +@EnableDiscoveryClient public class ExpApplication { public static void main(String[] args) { diff --git a/fund-exp/src/main/java/com/fundplatform/exp/controller/FundExpenseController.java b/fund-exp/src/main/java/com/fundplatform/exp/controller/FundExpenseController.java new file mode 100644 index 0000000..980df86 --- /dev/null +++ b/fund-exp/src/main/java/com/fundplatform/exp/controller/FundExpenseController.java @@ -0,0 +1,65 @@ +package com.fundplatform.exp.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.common.core.Result; +import com.fundplatform.exp.dto.FundExpenseDTO; +import com.fundplatform.exp.service.FundExpenseService; +import com.fundplatform.exp.vo.FundExpenseVO; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/exp/expense") +public class FundExpenseController { + + private final FundExpenseService expenseService; + + public FundExpenseController(FundExpenseService expenseService) { + this.expenseService = expenseService; + } + + @PostMapping + public Result create(@Valid @RequestBody FundExpenseDTO dto) { + return Result.success(expenseService.createExpense(dto)); + } + + @PutMapping + public Result update(@Valid @RequestBody FundExpenseDTO dto) { + return Result.success(expenseService.updateExpense(dto)); + } + + @GetMapping("/{id}") + public Result getById(@PathVariable Long id) { + return Result.success(expenseService.getExpenseById(id)); + } + + @GetMapping("/page") + public Result> page( + @RequestParam(defaultValue = "1") int pageNum, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) String title, + @RequestParam(required = false) Integer expenseType, + @RequestParam(required = false) Integer payStatus) { + return Result.success(expenseService.pageExpenses(pageNum, pageSize, title, expenseType, payStatus)); + } + + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + return Result.success(expenseService.deleteExpense(id)); + } + + @PutMapping("/{id}/approve") + public Result approve(@PathVariable Long id, @RequestParam(required = false) String comment) { + return Result.success(expenseService.approve(id, comment)); + } + + @PutMapping("/{id}/reject") + public Result reject(@PathVariable Long id, @RequestParam(required = false) String comment) { + return Result.success(expenseService.reject(id, comment)); + } + + @PutMapping("/{id}/confirm-pay") + public Result confirmPay(@PathVariable Long id, @RequestParam String payChannel, @RequestParam(required = false) String payVoucher) { + return Result.success(expenseService.confirmPay(id, payChannel, payVoucher)); + } +} diff --git a/fund-exp/src/main/java/com/fundplatform/exp/data/entity/FundExpense.java b/fund-exp/src/main/java/com/fundplatform/exp/data/entity/FundExpense.java new file mode 100644 index 0000000..bb126a4 --- /dev/null +++ b/fund-exp/src/main/java/com/fundplatform/exp/data/entity/FundExpense.java @@ -0,0 +1,256 @@ +package com.fundplatform.exp.data.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fundplatform.common.core.BaseEntity; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 支出管理实体 + */ +@TableName("fund_expense") +public class FundExpense extends BaseEntity { + + /** 支出单号 */ + private String expenseNo; + + /** 支出标题 */ + private String title; + + /** 支出金额 */ + private BigDecimal amount; + + /** 币种 */ + private String currency; + + /** 支出类型(1-日常支出 2-项目支出 3-工资发放 4-其他) */ + private Integer expenseType; + + /** 收款单位 */ + private String payeeName; + + /** 收款银行 */ + private String payeeBank; + + /** 收款账号 */ + private String payeeAccount; + + /** 支出日期 */ + private LocalDateTime expenseDate; + + /** 用途说明 */ + private String purpose; + + /** 关联用款申请ID */ + private Long requestId; + + /** 项目ID */ + private Long projectId; + + /** 客户ID */ + private Long customerId; + + /** 支付状态(0-待支付 1-已支付 2-支付失败) */ + private Integer payStatus; + + /** 支付时间 */ + private LocalDateTime payTime; + + /** 支付渠道 */ + private String payChannel; + + /** 支付凭证 */ + private String payVoucher; + + /** 审批状态 */ + private Integer approvalStatus; + + /** 审批人ID */ + private Long approverId; + + /** 审批时间 */ + private LocalDateTime approvalTime; + + /** 审批意见 */ + private String approvalComment; + + /** 附件URL */ + private String attachments; + + public String getExpenseNo() { + return expenseNo; + } + + public void setExpenseNo(String expenseNo) { + this.expenseNo = expenseNo; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public Integer getExpenseType() { + return expenseType; + } + + public void setExpenseType(Integer expenseType) { + this.expenseType = expenseType; + } + + public String getPayeeName() { + return payeeName; + } + + public void setPayeeName(String payeeName) { + this.payeeName = payeeName; + } + + public String getPayeeBank() { + return payeeBank; + } + + public void setPayeeBank(String payeeBank) { + this.payeeBank = payeeBank; + } + + public String getPayeeAccount() { + return payeeAccount; + } + + public void setPayeeAccount(String payeeAccount) { + this.payeeAccount = payeeAccount; + } + + public LocalDateTime getExpenseDate() { + return expenseDate; + } + + public void setExpenseDate(LocalDateTime expenseDate) { + this.expenseDate = expenseDate; + } + + public String getPurpose() { + return purpose; + } + + public void setPurpose(String purpose) { + this.purpose = purpose; + } + + public Long getRequestId() { + return requestId; + } + + public void setRequestId(Long requestId) { + this.requestId = requestId; + } + + public Long getProjectId() { + return projectId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(Long customerId) { + this.customerId = customerId; + } + + public Integer getPayStatus() { + return payStatus; + } + + public void setPayStatus(Integer payStatus) { + this.payStatus = payStatus; + } + + public LocalDateTime getPayTime() { + return payTime; + } + + public void setPayTime(LocalDateTime payTime) { + this.payTime = payTime; + } + + public String getPayChannel() { + return payChannel; + } + + public void setPayChannel(String payChannel) { + this.payChannel = payChannel; + } + + public String getPayVoucher() { + return payVoucher; + } + + public void setPayVoucher(String payVoucher) { + this.payVoucher = payVoucher; + } + + public Integer getApprovalStatus() { + return approvalStatus; + } + + public void setApprovalStatus(Integer approvalStatus) { + this.approvalStatus = approvalStatus; + } + + public Long getApproverId() { + return approverId; + } + + public void setApproverId(Long approverId) { + this.approverId = approverId; + } + + public LocalDateTime getApprovalTime() { + return approvalTime; + } + + public void setApprovalTime(LocalDateTime approvalTime) { + this.approvalTime = approvalTime; + } + + public String getApprovalComment() { + return approvalComment; + } + + public void setApprovalComment(String approvalComment) { + this.approvalComment = approvalComment; + } + + public String getAttachments() { + return attachments; + } + + public void setAttachments(String attachments) { + this.attachments = attachments; + } +} diff --git a/fund-exp/src/main/java/com/fundplatform/exp/data/mapper/FundExpenseMapper.java b/fund-exp/src/main/java/com/fundplatform/exp/data/mapper/FundExpenseMapper.java new file mode 100644 index 0000000..2a59f3e --- /dev/null +++ b/fund-exp/src/main/java/com/fundplatform/exp/data/mapper/FundExpenseMapper.java @@ -0,0 +1,9 @@ +package com.fundplatform.exp.data.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fundplatform.exp.data.entity.FundExpense; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface FundExpenseMapper extends BaseMapper { +} diff --git a/fund-exp/src/main/java/com/fundplatform/exp/data/service/FundExpenseDataService.java b/fund-exp/src/main/java/com/fundplatform/exp/data/service/FundExpenseDataService.java new file mode 100644 index 0000000..7a70031 --- /dev/null +++ b/fund-exp/src/main/java/com/fundplatform/exp/data/service/FundExpenseDataService.java @@ -0,0 +1,11 @@ +package com.fundplatform.exp.data.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fundplatform.exp.data.entity.FundExpense; +import com.fundplatform.exp.data.mapper.FundExpenseMapper; +import org.springframework.stereotype.Service; + +@Service +public class FundExpenseDataService extends ServiceImpl implements IService { +} diff --git a/fund-exp/src/main/java/com/fundplatform/exp/dto/FundExpenseDTO.java b/fund-exp/src/main/java/com/fundplatform/exp/dto/FundExpenseDTO.java new file mode 100644 index 0000000..5c82a43 --- /dev/null +++ b/fund-exp/src/main/java/com/fundplatform/exp/dto/FundExpenseDTO.java @@ -0,0 +1,70 @@ +package com.fundplatform.exp.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public class FundExpenseDTO { + + private Long id; + + @NotBlank(message = "支出标题不能为空") + private String title; + + @NotNull(message = "支出金额不能为空") + @Positive(message = "支出金额必须大于0") + private BigDecimal amount; + + private String currency = "CNY"; + + @NotNull(message = "支出类型不能为空") + private Integer expenseType; + + @NotBlank(message = "收款单位不能为空") + private String payeeName; + + private String payeeBank; + private String payeeAccount; + private LocalDateTime expenseDate; + private String purpose; + private Long requestId; + private Long projectId; + private Long customerId; + private String attachments; + private String remark; + + // getters and setters + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + public BigDecimal getAmount() { return amount; } + public void setAmount(BigDecimal amount) { this.amount = amount; } + public String getCurrency() { return currency; } + public void setCurrency(String currency) { this.currency = currency; } + public Integer getExpenseType() { return expenseType; } + public void setExpenseType(Integer expenseType) { this.expenseType = expenseType; } + public String getPayeeName() { return payeeName; } + public void setPayeeName(String payeeName) { this.payeeName = payeeName; } + public String getPayeeBank() { return payeeBank; } + public void setPayeeBank(String payeeBank) { this.payeeBank = payeeBank; } + public String getPayeeAccount() { return payeeAccount; } + public void setPayeeAccount(String payeeAccount) { this.payeeAccount = payeeAccount; } + public LocalDateTime getExpenseDate() { return expenseDate; } + public void setExpenseDate(LocalDateTime expenseDate) { this.expenseDate = expenseDate; } + public String getPurpose() { return purpose; } + public void setPurpose(String purpose) { this.purpose = purpose; } + public Long getRequestId() { return requestId; } + public void setRequestId(Long requestId) { this.requestId = requestId; } + public Long getProjectId() { return projectId; } + public void setProjectId(Long projectId) { this.projectId = projectId; } + public Long getCustomerId() { return customerId; } + public void setCustomerId(Long customerId) { this.customerId = customerId; } + public String getAttachments() { return attachments; } + public void setAttachments(String attachments) { this.attachments = attachments; } + public String getRemark() { return remark; } + public void setRemark(String remark) { this.remark = remark; } +} diff --git a/fund-exp/src/main/java/com/fundplatform/exp/service/FundExpenseService.java b/fund-exp/src/main/java/com/fundplatform/exp/service/FundExpenseService.java new file mode 100644 index 0000000..b23a134 --- /dev/null +++ b/fund-exp/src/main/java/com/fundplatform/exp/service/FundExpenseService.java @@ -0,0 +1,24 @@ +package com.fundplatform.exp.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.exp.dto.FundExpenseDTO; +import com.fundplatform.exp.vo.FundExpenseVO; + +public interface FundExpenseService { + + Long createExpense(FundExpenseDTO dto); + + boolean updateExpense(FundExpenseDTO dto); + + FundExpenseVO getExpenseById(Long id); + + Page pageExpenses(int pageNum, int pageSize, String title, Integer expenseType, Integer payStatus); + + boolean deleteExpense(Long id); + + boolean approve(Long id, String comment); + + boolean reject(Long id, String comment); + + boolean confirmPay(Long id, String payChannel, String payVoucher); +} diff --git a/fund-exp/src/main/java/com/fundplatform/exp/service/impl/FundExpenseServiceImpl.java b/fund-exp/src/main/java/com/fundplatform/exp/service/impl/FundExpenseServiceImpl.java new file mode 100644 index 0000000..bced6f0 --- /dev/null +++ b/fund-exp/src/main/java/com/fundplatform/exp/service/impl/FundExpenseServiceImpl.java @@ -0,0 +1,198 @@ +package com.fundplatform.exp.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.exp.data.entity.FundExpense; +import com.fundplatform.exp.data.service.FundExpenseDataService; +import com.fundplatform.exp.dto.FundExpenseDTO; +import com.fundplatform.exp.service.FundExpenseService; +import com.fundplatform.exp.vo.FundExpenseVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.atomic.AtomicInteger; + +@Service +public class FundExpenseServiceImpl implements FundExpenseService { + + private static final Logger log = LoggerFactory.getLogger(FundExpenseServiceImpl.class); + private static final AtomicInteger counter = new AtomicInteger(1); + + private final FundExpenseDataService expenseDataService; + + public FundExpenseServiceImpl(FundExpenseDataService expenseDataService) { + this.expenseDataService = expenseDataService; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createExpense(FundExpenseDTO dto) { + FundExpense expense = new FundExpense(); + expense.setExpenseNo(generateExpenseNo()); + expense.setTitle(dto.getTitle()); + expense.setAmount(dto.getAmount()); + expense.setCurrency(dto.getCurrency() != null ? dto.getCurrency() : "CNY"); + expense.setExpenseType(dto.getExpenseType()); + expense.setPayeeName(dto.getPayeeName()); + expense.setPayeeBank(dto.getPayeeBank()); + expense.setPayeeAccount(dto.getPayeeAccount()); + expense.setExpenseDate(dto.getExpenseDate() != null ? dto.getExpenseDate() : LocalDateTime.now()); + expense.setPurpose(dto.getPurpose()); + expense.setRequestId(dto.getRequestId()); + expense.setProjectId(dto.getProjectId()); + expense.setCustomerId(dto.getCustomerId()); + expense.setPayStatus(0); + expense.setApprovalStatus(0); + expense.setAttachments(dto.getAttachments()); + expense.setDeleted(0); + expense.setCreatedTime(LocalDateTime.now()); + + expenseDataService.save(expense); + log.info("创建支出记录成功: id={}, expenseNo={}", expense.getId(), expense.getExpenseNo()); + return expense.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateExpense(FundExpenseDTO dto) { + if (dto.getId() == null) throw new RuntimeException("支出ID不能为空"); + FundExpense existing = expenseDataService.getById(dto.getId()); + if (existing == null || existing.getDeleted() == 1) throw new RuntimeException("支出记录不存在"); + if (existing.getPayStatus() != 0) throw new RuntimeException("当前状态不允许修改"); + + FundExpense expense = new FundExpense(); + expense.setId(dto.getId()); + expense.setTitle(dto.getTitle()); + expense.setAmount(dto.getAmount()); + expense.setPurpose(dto.getPurpose()); + expense.setAttachments(dto.getAttachments()); + expense.setUpdatedTime(LocalDateTime.now()); + return expenseDataService.updateById(expense); + } + + @Override + public FundExpenseVO getExpenseById(Long id) { + FundExpense expense = expenseDataService.getById(id); + if (expense == null || expense.getDeleted() == 1) throw new RuntimeException("支出记录不存在"); + return convertToVO(expense); + } + + @Override + public Page pageExpenses(int pageNum, int pageSize, String title, Integer expenseType, Integer payStatus) { + Page page = new Page<>(pageNum, pageSize); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(FundExpense::getDeleted, 0); + if (StringUtils.hasText(title)) wrapper.like(FundExpense::getTitle, title); + if (expenseType != null) wrapper.eq(FundExpense::getExpenseType, expenseType); + if (payStatus != null) wrapper.eq(FundExpense::getPayStatus, payStatus); + wrapper.orderByDesc(FundExpense::getCreatedTime); + + Page expensePage = expenseDataService.page(page, wrapper); + Page voPage = new Page<>(expensePage.getCurrent(), expensePage.getSize(), expensePage.getTotal()); + voPage.setRecords(expensePage.getRecords().stream().map(this::convertToVO).toList()); + return voPage; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteExpense(Long id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundExpense::getId, id).set(FundExpense::getDeleted, 1).set(FundExpense::getUpdatedTime, LocalDateTime.now()); + return expenseDataService.update(wrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean approve(Long id, String comment) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundExpense::getId, id).set(FundExpense::getApprovalStatus, 2) + .set(FundExpense::getApprovalTime, LocalDateTime.now()) + .set(FundExpense::getApprovalComment, comment) + .set(FundExpense::getUpdatedTime, LocalDateTime.now()); + return expenseDataService.update(wrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean reject(Long id, String comment) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundExpense::getId, id).set(FundExpense::getApprovalStatus, 3) + .set(FundExpense::getApprovalTime, LocalDateTime.now()) + .set(FundExpense::getApprovalComment, comment) + .set(FundExpense::getUpdatedTime, LocalDateTime.now()); + return expenseDataService.update(wrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean confirmPay(Long id, String payChannel, String payVoucher) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundExpense::getId, id).set(FundExpense::getPayStatus, 1) + .set(FundExpense::getPayTime, LocalDateTime.now()) + .set(FundExpense::getPayChannel, payChannel) + .set(FundExpense::getPayVoucher, payVoucher) + .set(FundExpense::getUpdatedTime, LocalDateTime.now()); + return expenseDataService.update(wrapper); + } + + private String generateExpenseNo() { + String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + int seq = counter.getAndIncrement(); + return String.format("EXP%s%04d", dateStr, seq); + } + + private FundExpenseVO convertToVO(FundExpense e) { + FundExpenseVO vo = new FundExpenseVO(); + vo.setId(e.getId()); + vo.setExpenseNo(e.getExpenseNo()); + vo.setTitle(e.getTitle()); + vo.setAmount(e.getAmount()); + vo.setCurrency(e.getCurrency()); + vo.setExpenseType(e.getExpenseType()); + vo.setExpenseTypeName(getExpenseTypeName(e.getExpenseType())); + vo.setPayeeName(e.getPayeeName()); + vo.setPayeeBank(e.getPayeeBank()); + vo.setPayeeAccount(e.getPayeeAccount()); + vo.setExpenseDate(e.getExpenseDate()); + vo.setPurpose(e.getPurpose()); + vo.setRequestId(e.getRequestId()); + vo.setProjectId(e.getProjectId()); + vo.setCustomerId(e.getCustomerId()); + vo.setPayStatus(e.getPayStatus()); + vo.setPayStatusName(getPayStatusName(e.getPayStatus())); + vo.setPayTime(e.getPayTime()); + vo.setPayChannel(e.getPayChannel()); + vo.setPayVoucher(e.getPayVoucher()); + vo.setApprovalStatus(e.getApprovalStatus()); + vo.setApprovalStatusName(getApprovalStatusName(e.getApprovalStatus())); + vo.setApprovalTime(e.getApprovalTime()); + vo.setApprovalComment(e.getApprovalComment()); + vo.setAttachments(e.getAttachments()); + vo.setTenantId(e.getTenantId()); + vo.setCreatedBy(e.getCreatedBy()); + vo.setCreatedTime(e.getCreatedTime()); + return vo; + } + + private String getExpenseTypeName(Integer type) { + if (type == null) return ""; + return switch (type) { case 1 -> "日常支出"; case 2 -> "项目支出"; case 3 -> "工资发放"; case 4 -> "其他"; default -> ""; }; + } + + private String getPayStatusName(Integer status) { + if (status == null) return ""; + return switch (status) { case 0 -> "待支付"; case 1 -> "已支付"; case 2 -> "支付失败"; default -> ""; }; + } + + private String getApprovalStatusName(Integer status) { + if (status == null) return ""; + return switch (status) { case 0 -> "待审批"; case 1 -> "审批中"; case 2 -> "审批通过"; case 3 -> "审批拒绝"; default -> ""; }; + } +} diff --git a/fund-exp/src/main/java/com/fundplatform/exp/vo/FundExpenseVO.java b/fund-exp/src/main/java/com/fundplatform/exp/vo/FundExpenseVO.java new file mode 100644 index 0000000..9014e99 --- /dev/null +++ b/fund-exp/src/main/java/com/fundplatform/exp/vo/FundExpenseVO.java @@ -0,0 +1,97 @@ +package com.fundplatform.exp.vo; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public class FundExpenseVO { + + private Long id; + private String expenseNo; + private String title; + private BigDecimal amount; + private String currency; + private Integer expenseType; + private String expenseTypeName; + private String payeeName; + private String payeeBank; + private String payeeAccount; + private LocalDateTime expenseDate; + private String purpose; + private Long requestId; + private Long projectId; + private Long customerId; + private Integer payStatus; + private String payStatusName; + private LocalDateTime payTime; + private String payChannel; + private String payVoucher; + private Integer approvalStatus; + private String approvalStatusName; + private Long approverId; + private LocalDateTime approvalTime; + private String approvalComment; + private String attachments; + private Long tenantId; + private Long createdBy; + private LocalDateTime createdTime; + + // getters and setters + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + public String getExpenseNo() { return expenseNo; } + public void setExpenseNo(String expenseNo) { this.expenseNo = expenseNo; } + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + public BigDecimal getAmount() { return amount; } + public void setAmount(BigDecimal amount) { this.amount = amount; } + public String getCurrency() { return currency; } + public void setCurrency(String currency) { this.currency = currency; } + public Integer getExpenseType() { return expenseType; } + public void setExpenseType(Integer expenseType) { this.expenseType = expenseType; } + public String getExpenseTypeName() { return expenseTypeName; } + public void setExpenseTypeName(String expenseTypeName) { this.expenseTypeName = expenseTypeName; } + public String getPayeeName() { return payeeName; } + public void setPayeeName(String payeeName) { this.payeeName = payeeName; } + public String getPayeeBank() { return payeeBank; } + public void setPayeeBank(String payeeBank) { this.payeeBank = payeeBank; } + public String getPayeeAccount() { return payeeAccount; } + public void setPayeeAccount(String payeeAccount) { this.payeeAccount = payeeAccount; } + public LocalDateTime getExpenseDate() { return expenseDate; } + public void setExpenseDate(LocalDateTime expenseDate) { this.expenseDate = expenseDate; } + public String getPurpose() { return purpose; } + public void setPurpose(String purpose) { this.purpose = purpose; } + public Long getRequestId() { return requestId; } + public void setRequestId(Long requestId) { this.requestId = requestId; } + public Long getProjectId() { return projectId; } + public void setProjectId(Long projectId) { this.projectId = projectId; } + public Long getCustomerId() { return customerId; } + public void setCustomerId(Long customerId) { this.customerId = customerId; } + public Integer getPayStatus() { return payStatus; } + public void setPayStatus(Integer payStatus) { this.payStatus = payStatus; } + public String getPayStatusName() { return payStatusName; } + public void setPayStatusName(String payStatusName) { this.payStatusName = payStatusName; } + public LocalDateTime getPayTime() { return payTime; } + public void setPayTime(LocalDateTime payTime) { this.payTime = payTime; } + public String getPayChannel() { return payChannel; } + public void setPayChannel(String payChannel) { this.payChannel = payChannel; } + public String getPayVoucher() { return payVoucher; } + public void setPayVoucher(String payVoucher) { this.payVoucher = payVoucher; } + public Integer getApprovalStatus() { return approvalStatus; } + public void setApprovalStatus(Integer approvalStatus) { this.approvalStatus = approvalStatus; } + public String getApprovalStatusName() { return approvalStatusName; } + public void setApprovalStatusName(String approvalStatusName) { this.approvalStatusName = approvalStatusName; } + public Long getApproverId() { return approverId; } + public void setApproverId(Long approverId) { this.approverId = approverId; } + public LocalDateTime getApprovalTime() { return approvalTime; } + public void setApprovalTime(LocalDateTime approvalTime) { this.approvalTime = approvalTime; } + public String getApprovalComment() { return approvalComment; } + public void setApprovalComment(String approvalComment) { this.approvalComment = approvalComment; } + public String getAttachments() { return attachments; } + public void setAttachments(String attachments) { this.attachments = attachments; } + public Long getTenantId() { return tenantId; } + public void setTenantId(Long tenantId) { this.tenantId = tenantId; } + public Long getCreatedBy() { return createdBy; } + public void setCreatedBy(Long createdBy) { this.createdBy = createdBy; } + public LocalDateTime getCreatedTime() { return createdTime; } + public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; } +} diff --git a/fund-exp/src/main/resources/application.yml b/fund-exp/src/main/resources/application.yml index f578e4b..b9cb336 100644 --- a/fund-exp/src/main/resources/application.yml +++ b/fund-exp/src/main/resources/application.yml @@ -4,3 +4,10 @@ server: spring: application: name: fund-exp + + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: fund-platform + group: DEFAULT_GROUP diff --git a/fund-file/pom.xml b/fund-file/pom.xml index 4fdc079..391d385 100644 --- a/fund-file/pom.xml +++ b/fund-file/pom.xml @@ -33,6 +33,11 @@ com.mysql mysql-connector-j + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + diff --git a/fund-file/src/main/java/com/fundplatform/file/FileApplication.java b/fund-file/src/main/java/com/fundplatform/file/FileApplication.java index 0ac80c0..a7e7262 100644 --- a/fund-file/src/main/java/com/fundplatform/file/FileApplication.java +++ b/fund-file/src/main/java/com/fundplatform/file/FileApplication.java @@ -2,8 +2,10 @@ package com.fundplatform.file; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication +@EnableDiscoveryClient public class FileApplication { public static void main(String[] args) { diff --git a/fund-file/src/main/resources/application.yml b/fund-file/src/main/resources/application.yml index 23834e6..3211240 100644 --- a/fund-file/src/main/resources/application.yml +++ b/fund-file/src/main/resources/application.yml @@ -4,3 +4,10 @@ server: spring: application: name: fund-file + + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: fund-platform + group: DEFAULT_GROUP diff --git a/fund-gateway/pom.xml b/fund-gateway/pom.xml index 7fcaf47..cfeac27 100644 --- a/fund-gateway/pom.xml +++ b/fund-gateway/pom.xml @@ -34,6 +34,31 @@ spring-cloud-starter-gateway 4.0.0 + + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + + + org.springframework.boot + spring-boot-starter-data-redis-reactive + diff --git a/fund-gateway/src/main/java/com/fundplatform/gateway/config/RateLimitConfig.java b/fund-gateway/src/main/java/com/fundplatform/gateway/config/RateLimitConfig.java new file mode 100644 index 0000000..c276f75 --- /dev/null +++ b/fund-gateway/src/main/java/com/fundplatform/gateway/config/RateLimitConfig.java @@ -0,0 +1,45 @@ +package com.fundplatform.gateway.config; + +import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import reactor.core.publisher.Mono; + +/** + * 限流配置 + */ +@Configuration +public class RateLimitConfig { + + /** + * 基于IP的限流Key解析器 + */ + @Bean + public KeyResolver ipKeyResolver() { + return exchange -> { + String ip = exchange.getRequest().getRemoteAddress() != null + ? exchange.getRequest().getRemoteAddress().getAddress().getHostAddress() + : "unknown"; + return Mono.just(ip); + }; + } + + /** + * 基于用户ID的限流Key解析器 + */ + @Bean + public KeyResolver userKeyResolver() { + return exchange -> { + String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id"); + return Mono.just(userId != null ? userId : "anonymous"); + }; + } + + /** + * 基于API路径的限流Key解析器 + */ + @Bean + public KeyResolver apiKeyResolver() { + return exchange -> Mono.just(exchange.getRequest().getPath().value()); + } +} diff --git a/fund-gateway/src/main/java/com/fundplatform/gateway/filter/GlobalLogFilter.java b/fund-gateway/src/main/java/com/fundplatform/gateway/filter/GlobalLogFilter.java new file mode 100644 index 0000000..46a38ab --- /dev/null +++ b/fund-gateway/src/main/java/com/fundplatform/gateway/filter/GlobalLogFilter.java @@ -0,0 +1,88 @@ +package com.fundplatform.gateway.filter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.UUID; + +/** + * 全局日志过滤器 + * 记录所有请求日志,生成TraceId + */ +@Component +public class GlobalLogFilter implements GlobalFilter, Ordered { + + private static final Logger logger = LoggerFactory.getLogger(GlobalLogFilter.class); + private static final String TRACE_ID = "X-Trace-Id"; + private static final String START_TIME = "startTime"; + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 生成TraceId + String traceId = UUID.randomUUID().toString().replace("-", ""); + + // 记录开始时间 + exchange.getAttributes().put(START_TIME, System.currentTimeMillis()); + + // 将TraceId写入请求头 + ServerHttpRequest request = exchange.getRequest().mutate() + .header(TRACE_ID, traceId) + .build(); + + // 记录请求日志 + String method = request.getMethod().name(); + String path = request.getURI().getPath(); + String clientIp = getClientIp(request); + + logger.info("[{}] Request: {} {} from {}", traceId, method, path, clientIp); + + // 继续执行 + return chain.filter(exchange.mutate().request(request).build()) + .then(Mono.fromRunnable(() -> { + // 计算耗时 + Long startTime = exchange.getAttribute(START_TIME); + if (startTime != null) { + long duration = System.currentTimeMillis() - startTime; + int statusCode = exchange.getResponse().getStatusCode() != null + ? exchange.getResponse().getStatusCode().value() + : 0; + + logger.info("[{}] Response: {} {} - {} ({}ms)", + traceId, method, path, statusCode, duration); + } + })); + } + + /** + * 获取客户端IP + */ + private String getClientIp(ServerHttpRequest request) { + String ip = request.getHeaders().getFirst("X-Forwarded-For"); + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeaders().getFirst("X-Real-IP"); + } + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddress() != null + ? request.getRemoteAddress().getAddress().getHostAddress() + : "unknown"; + } + // 多个代理时取第一个IP + if (ip != null && ip.contains(",")) { + ip = ip.split(",")[0].trim(); + } + return ip; + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } +} diff --git a/fund-gateway/src/main/java/com/fundplatform/gateway/filter/JwtAuthFilter.java b/fund-gateway/src/main/java/com/fundplatform/gateway/filter/JwtAuthFilter.java new file mode 100644 index 0000000..4a7d027 --- /dev/null +++ b/fund-gateway/src/main/java/com/fundplatform/gateway/filter/JwtAuthFilter.java @@ -0,0 +1,158 @@ +package com.fundplatform.gateway.filter; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fundplatform.common.core.Result; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +/** + * JWT鉴权过滤器 + * 验证JWT Token,提取用户信息 + */ +@Component +public class JwtAuthFilter implements GlobalFilter, Ordered { + + private static final Logger logger = LoggerFactory.getLogger(JwtAuthFilter.class); + private static final String SECRET_KEY = "fundplatform-secret-key-for-jwt-token-generation-min-256-bits"; + private static final String TOKEN_PREFIX = "Bearer "; + private static final String USER_ID_HEADER = "X-User-Id"; + private static final String USERNAME_HEADER = "X-Username"; + private static final String TENANT_ID_HEADER = "X-Tenant-Id"; + + // 白名单路径(不需要token验证) + private static final List WHITE_LIST = Arrays.asList( + "/sys/api/v1/auth/login", + "/sys/api/v1/sys/health", + "/cust/api/v1/cust/health", + "/proj/api/v1/proj/health" + ); + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + String path = request.getURI().getPath(); + + // 白名单路径直接放行 + if (isWhiteListed(path)) { + return chain.filter(exchange); + } + + // 获取Token + String token = getToken(request); + if (token == null) { + return unauthorized(exchange, "缺少认证Token"); + } + + try { + // 验证Token + Claims claims = validateToken(token); + if (claims == null) { + return unauthorized(exchange, "Token无效或已过期"); + } + + // 提取用户信息 + Long userId = claims.get("userId", Long.class); + String username = claims.get("username", String.class); + Long tenantId = claims.get("tenantId", Long.class); + + // 将用户信息写入请求头 + ServerHttpRequest mutatedRequest = request.mutate() + .header(USER_ID_HEADER, String.valueOf(userId)) + .header(USERNAME_HEADER, username) + .header(TENANT_ID_HEADER, String.valueOf(tenantId)) + .build(); + + logger.debug("Token验证通过: userId={}, username={}, tenantId={}", userId, username, tenantId); + + return chain.filter(exchange.mutate().request(mutatedRequest).build()); + + } catch (Exception e) { + logger.error("Token验证失败: {}", e.getMessage()); + return unauthorized(exchange, "Token验证失败: " + e.getMessage()); + } + } + + /** + * 判断是否白名单路径 + */ + private boolean isWhiteListed(String path) { + return WHITE_LIST.stream().anyMatch(path::startsWith); + } + + /** + * 从请求头获取Token + */ + private String getToken(ServerHttpRequest request) { + String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION); + if (authHeader != null && authHeader.startsWith(TOKEN_PREFIX)) { + return authHeader.substring(TOKEN_PREFIX.length()); + } + return null; + } + + /** + * 验证Token + */ + private Claims validateToken(String token) { + try { + SecretKey key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8)); + return Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token) + .getBody(); + } catch (Exception e) { + logger.error("Token解析失败: {}", e.getMessage()); + return null; + } + } + + /** + * 返回未授权响应 + */ + private Mono unauthorized(ServerWebExchange exchange, String message) { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.UNAUTHORIZED); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + + Result result = Result.error(401, message); + String body; + try { + body = objectMapper.writeValueAsString(result); + } catch (JsonProcessingException e) { + body = "{\"code\":401,\"message\":\"Unauthorized\",\"success\":false}"; + } + + DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8)); + return response.writeWith(Mono.just(buffer)); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE + 1; + } +} diff --git a/fund-gateway/src/main/resources/application.yml b/fund-gateway/src/main/resources/application.yml index 2e4bb1f..39a60cf 100644 --- a/fund-gateway/src/main/resources/application.yml +++ b/fund-gateway/src/main/resources/application.yml @@ -4,10 +4,27 @@ server: spring: application: name: fund-gateway + + # Redis配置(用于限流) + data: + redis: + host: localhost + port: 6379 + password: zjf@123456 + database: 1 + cloud: compatibility-verifier: enabled: false gateway: + # 默认限流配置 + default-filters: + - name: RequestRateLimiter + args: + redis-rate-limiter.replenishRate: 100 # 每秒补充令牌数 + redis-rate-limiter.burstCapacity: 200 # 令牌桶最大容量 + key-resolver: "#{@ipKeyResolver}" + routes: # 系统管理服务 - id: fund-sys diff --git a/fund-proj/pom.xml b/fund-proj/pom.xml index fdd0278..d048a8f 100644 --- a/fund-proj/pom.xml +++ b/fund-proj/pom.xml @@ -33,6 +33,11 @@ com.mysql mysql-connector-j + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + diff --git a/fund-proj/src/main/java/com/fundplatform/proj/ProjApplication.java b/fund-proj/src/main/java/com/fundplatform/proj/ProjApplication.java index 84575d3..e400841 100644 --- a/fund-proj/src/main/java/com/fundplatform/proj/ProjApplication.java +++ b/fund-proj/src/main/java/com/fundplatform/proj/ProjApplication.java @@ -2,8 +2,10 @@ package com.fundplatform.proj; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication(scanBasePackages = {"com.fundplatform.proj", "com.fundplatform.common"}) +@EnableDiscoveryClient public class ProjApplication { public static void main(String[] args) { diff --git a/fund-proj/src/main/resources/application.yml b/fund-proj/src/main/resources/application.yml index 307ee7d..00c2da0 100644 --- a/fund-proj/src/main/resources/application.yml +++ b/fund-proj/src/main/resources/application.yml @@ -5,6 +5,13 @@ spring: application: name: fund-proj + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: fund-platform + group: DEFAULT_GROUP + datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/fund_proj?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai diff --git a/fund-receipt/src/main/java/com/fundplatform/receipt/ReceiptApplication.java b/fund-receipt/src/main/java/com/fundplatform/receipt/ReceiptApplication.java index 0c520fc..e900c41 100644 --- a/fund-receipt/src/main/java/com/fundplatform/receipt/ReceiptApplication.java +++ b/fund-receipt/src/main/java/com/fundplatform/receipt/ReceiptApplication.java @@ -2,8 +2,10 @@ package com.fundplatform.receipt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication +@EnableDiscoveryClient public class ReceiptApplication { public static void main(String[] args) { diff --git a/fund-receipt/src/main/java/com/fundplatform/receipt/controller/FundReceiptController.java b/fund-receipt/src/main/java/com/fundplatform/receipt/controller/FundReceiptController.java new file mode 100644 index 0000000..b98bfd8 --- /dev/null +++ b/fund-receipt/src/main/java/com/fundplatform/receipt/controller/FundReceiptController.java @@ -0,0 +1,60 @@ +package com.fundplatform.receipt.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.common.core.Result; +import com.fundplatform.receipt.dto.FundReceiptDTO; +import com.fundplatform.receipt.service.FundReceiptService; +import com.fundplatform.receipt.vo.FundReceiptVO; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/receipt/receipt") +public class FundReceiptController { + + private final FundReceiptService receiptService; + + public FundReceiptController(FundReceiptService receiptService) { + this.receiptService = receiptService; + } + + @PostMapping + public Result create(@Valid @RequestBody FundReceiptDTO dto) { + return Result.success(receiptService.createReceipt(dto)); + } + + @PutMapping + public Result update(@Valid @RequestBody FundReceiptDTO dto) { + return Result.success(receiptService.updateReceipt(dto)); + } + + @GetMapping("/{id}") + public Result getById(@PathVariable Long id) { + return Result.success(receiptService.getReceiptById(id)); + } + + @GetMapping("/page") + public Result> page( + @RequestParam(defaultValue = "1") int pageNum, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) String title, + @RequestParam(required = false) Integer receiptType, + @RequestParam(required = false) Integer receiptStatus) { + return Result.success(receiptService.pageReceipts(pageNum, pageSize, title, receiptType, receiptStatus)); + } + + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + return Result.success(receiptService.deleteReceipt(id)); + } + + @PutMapping("/{id}/confirm") + public Result confirm(@PathVariable Long id) { + return Result.success(receiptService.confirm(id)); + } + + @PutMapping("/{id}/write-off") + public Result writeOff(@PathVariable Long id) { + return Result.success(receiptService.writeOff(id)); + } +} diff --git a/fund-receipt/src/main/java/com/fundplatform/receipt/data/entity/FundReceipt.java b/fund-receipt/src/main/java/com/fundplatform/receipt/data/entity/FundReceipt.java new file mode 100644 index 0000000..4366fb0 --- /dev/null +++ b/fund-receipt/src/main/java/com/fundplatform/receipt/data/entity/FundReceipt.java @@ -0,0 +1,115 @@ +package com.fundplatform.receipt.data.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fundplatform.common.core.BaseEntity; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 收款管理实体 + */ +@TableName("fund_receipt") +public class FundReceipt extends BaseEntity { + + /** 收款单号 */ + private String receiptNo; + + /** 收款标题 */ + private String title; + + /** 收款金额 */ + private BigDecimal amount; + + /** 币种 */ + private String currency; + + /** 收款类型(1-项目收款 2-服务费 3-利息收入 4-其他) */ + private Integer receiptType; + + /** 付款单位 */ + private String payerName; + + /** 付款银行 */ + private String payerBank; + + /** 付款账号 */ + private String payerAccount; + + /** 收款日期 */ + private LocalDateTime receiptDate; + + /** 用途说明 */ + private String purpose; + + /** 关联项目ID */ + private Long projectId; + + /** 关联客户ID */ + private Long customerId; + + /** 收款状态(0-待确认 1-已确认 2-已核销) */ + private Integer receiptStatus; + + /** 确认时间 */ + private LocalDateTime confirmTime; + + /** 确认人ID */ + private Long confirmBy; + + /** 核销时间 */ + private LocalDateTime writeOffTime; + + /** 核销人ID */ + private Long writeOffBy; + + /** 收款凭证 */ + private String voucher; + + /** 发票号 */ + private String invoiceNo; + + /** 附件URL */ + private String attachments; + + public String getReceiptNo() { return receiptNo; } + public void setReceiptNo(String receiptNo) { this.receiptNo = receiptNo; } + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + public BigDecimal getAmount() { return amount; } + public void setAmount(BigDecimal amount) { this.amount = amount; } + public String getCurrency() { return currency; } + public void setCurrency(String currency) { this.currency = currency; } + public Integer getReceiptType() { return receiptType; } + public void setReceiptType(Integer receiptType) { this.receiptType = receiptType; } + public String getPayerName() { return payerName; } + public void setPayerName(String payerName) { this.payerName = payerName; } + public String getPayerBank() { return payerBank; } + public void setPayerBank(String payerBank) { this.payerBank = payerBank; } + public String getPayerAccount() { return payerAccount; } + public void setPayerAccount(String payerAccount) { this.payerAccount = payerAccount; } + public LocalDateTime getReceiptDate() { return receiptDate; } + public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; } + public String getPurpose() { return purpose; } + public void setPurpose(String purpose) { this.purpose = purpose; } + public Long getProjectId() { return projectId; } + public void setProjectId(Long projectId) { this.projectId = projectId; } + public Long getCustomerId() { return customerId; } + public void setCustomerId(Long customerId) { this.customerId = customerId; } + public Integer getReceiptStatus() { return receiptStatus; } + public void setReceiptStatus(Integer receiptStatus) { this.receiptStatus = receiptStatus; } + public LocalDateTime getConfirmTime() { return confirmTime; } + public void setConfirmTime(LocalDateTime confirmTime) { this.confirmTime = confirmTime; } + public Long getConfirmBy() { return confirmBy; } + public void setConfirmBy(Long confirmBy) { this.confirmBy = confirmBy; } + public LocalDateTime getWriteOffTime() { return writeOffTime; } + public void setWriteOffTime(LocalDateTime writeOffTime) { this.writeOffTime = writeOffTime; } + public Long getWriteOffBy() { return writeOffBy; } + public void setWriteOffBy(Long writeOffBy) { this.writeOffBy = writeOffBy; } + public String getVoucher() { return voucher; } + public void setVoucher(String voucher) { this.voucher = voucher; } + public String getInvoiceNo() { return invoiceNo; } + public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; } + public String getAttachments() { return attachments; } + public void setAttachments(String attachments) { this.attachments = attachments; } +} diff --git a/fund-receipt/src/main/java/com/fundplatform/receipt/data/mapper/FundReceiptMapper.java b/fund-receipt/src/main/java/com/fundplatform/receipt/data/mapper/FundReceiptMapper.java new file mode 100644 index 0000000..4fa1459 --- /dev/null +++ b/fund-receipt/src/main/java/com/fundplatform/receipt/data/mapper/FundReceiptMapper.java @@ -0,0 +1,9 @@ +package com.fundplatform.receipt.data.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fundplatform.receipt.data.entity.FundReceipt; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface FundReceiptMapper extends BaseMapper { +} diff --git a/fund-receipt/src/main/java/com/fundplatform/receipt/data/service/FundReceiptDataService.java b/fund-receipt/src/main/java/com/fundplatform/receipt/data/service/FundReceiptDataService.java new file mode 100644 index 0000000..f82005e --- /dev/null +++ b/fund-receipt/src/main/java/com/fundplatform/receipt/data/service/FundReceiptDataService.java @@ -0,0 +1,11 @@ +package com.fundplatform.receipt.data.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fundplatform.receipt.data.entity.FundReceipt; +import com.fundplatform.receipt.data.mapper.FundReceiptMapper; +import org.springframework.stereotype.Service; + +@Service +public class FundReceiptDataService extends ServiceImpl implements IService { +} diff --git a/fund-receipt/src/main/java/com/fundplatform/receipt/dto/FundReceiptDTO.java b/fund-receipt/src/main/java/com/fundplatform/receipt/dto/FundReceiptDTO.java new file mode 100644 index 0000000..14e3cdd --- /dev/null +++ b/fund-receipt/src/main/java/com/fundplatform/receipt/dto/FundReceiptDTO.java @@ -0,0 +1,71 @@ +package com.fundplatform.receipt.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public class FundReceiptDTO { + + private Long id; + + @NotBlank(message = "收款标题不能为空") + private String title; + + @NotNull(message = "收款金额不能为空") + @Positive(message = "收款金额必须大于0") + private BigDecimal amount; + + private String currency = "CNY"; + + @NotNull(message = "收款类型不能为空") + private Integer receiptType; + + @NotBlank(message = "付款单位不能为空") + private String payerName; + + private String payerBank; + private String payerAccount; + private LocalDateTime receiptDate; + private String purpose; + private Long projectId; + private Long customerId; + private String invoiceNo; + private String voucher; + private String attachments; + private String remark; + + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + public BigDecimal getAmount() { return amount; } + public void setAmount(BigDecimal amount) { this.amount = amount; } + public String getCurrency() { return currency; } + public void setCurrency(String currency) { this.currency = currency; } + public Integer getReceiptType() { return receiptType; } + public void setReceiptType(Integer receiptType) { this.receiptType = receiptType; } + public String getPayerName() { return payerName; } + public void setPayerName(String payerName) { this.payerName = payerName; } + public String getPayerBank() { return payerBank; } + public void setPayerBank(String payerBank) { this.payerBank = payerBank; } + public String getPayerAccount() { return payerAccount; } + public void setPayerAccount(String payerAccount) { this.payerAccount = payerAccount; } + public LocalDateTime getReceiptDate() { return receiptDate; } + public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; } + public String getPurpose() { return purpose; } + public void setPurpose(String purpose) { this.purpose = purpose; } + public Long getProjectId() { return projectId; } + public void setProjectId(Long projectId) { this.projectId = projectId; } + public Long getCustomerId() { return customerId; } + public void setCustomerId(Long customerId) { this.customerId = customerId; } + public String getInvoiceNo() { return invoiceNo; } + public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; } + public String getVoucher() { return voucher; } + public void setVoucher(String voucher) { this.voucher = voucher; } + public String getAttachments() { return attachments; } + public void setAttachments(String attachments) { this.attachments = attachments; } + public String getRemark() { return remark; } + public void setRemark(String remark) { this.remark = remark; } +} diff --git a/fund-receipt/src/main/java/com/fundplatform/receipt/service/FundReceiptService.java b/fund-receipt/src/main/java/com/fundplatform/receipt/service/FundReceiptService.java new file mode 100644 index 0000000..fa3ec46 --- /dev/null +++ b/fund-receipt/src/main/java/com/fundplatform/receipt/service/FundReceiptService.java @@ -0,0 +1,22 @@ +package com.fundplatform.receipt.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.receipt.dto.FundReceiptDTO; +import com.fundplatform.receipt.vo.FundReceiptVO; + +public interface FundReceiptService { + + Long createReceipt(FundReceiptDTO dto); + + boolean updateReceipt(FundReceiptDTO dto); + + FundReceiptVO getReceiptById(Long id); + + Page pageReceipts(int pageNum, int pageSize, String title, Integer receiptType, Integer receiptStatus); + + boolean deleteReceipt(Long id); + + boolean confirm(Long id); + + boolean writeOff(Long id); +} diff --git a/fund-receipt/src/main/java/com/fundplatform/receipt/service/impl/FundReceiptServiceImpl.java b/fund-receipt/src/main/java/com/fundplatform/receipt/service/impl/FundReceiptServiceImpl.java new file mode 100644 index 0000000..91c8d43 --- /dev/null +++ b/fund-receipt/src/main/java/com/fundplatform/receipt/service/impl/FundReceiptServiceImpl.java @@ -0,0 +1,178 @@ +package com.fundplatform.receipt.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.receipt.data.entity.FundReceipt; +import com.fundplatform.receipt.data.service.FundReceiptDataService; +import com.fundplatform.receipt.dto.FundReceiptDTO; +import com.fundplatform.receipt.service.FundReceiptService; +import com.fundplatform.receipt.vo.FundReceiptVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.atomic.AtomicInteger; + +@Service +public class FundReceiptServiceImpl implements FundReceiptService { + + private static final Logger log = LoggerFactory.getLogger(FundReceiptServiceImpl.class); + private static final AtomicInteger counter = new AtomicInteger(1); + + private final FundReceiptDataService receiptDataService; + + public FundReceiptServiceImpl(FundReceiptDataService receiptDataService) { + this.receiptDataService = receiptDataService; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createReceipt(FundReceiptDTO dto) { + FundReceipt receipt = new FundReceipt(); + receipt.setReceiptNo(generateReceiptNo()); + receipt.setTitle(dto.getTitle()); + receipt.setAmount(dto.getAmount()); + receipt.setCurrency(dto.getCurrency() != null ? dto.getCurrency() : "CNY"); + receipt.setReceiptType(dto.getReceiptType()); + receipt.setPayerName(dto.getPayerName()); + receipt.setPayerBank(dto.getPayerBank()); + receipt.setPayerAccount(dto.getPayerAccount()); + receipt.setReceiptDate(dto.getReceiptDate() != null ? dto.getReceiptDate() : LocalDateTime.now()); + receipt.setPurpose(dto.getPurpose()); + receipt.setProjectId(dto.getProjectId()); + receipt.setCustomerId(dto.getCustomerId()); + receipt.setReceiptStatus(0); + receipt.setInvoiceNo(dto.getInvoiceNo()); + receipt.setVoucher(dto.getVoucher()); + receipt.setAttachments(dto.getAttachments()); + receipt.setDeleted(0); + receipt.setCreatedTime(LocalDateTime.now()); + + receiptDataService.save(receipt); + log.info("创建收款记录成功: id={}, receiptNo={}", receipt.getId(), receipt.getReceiptNo()); + return receipt.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateReceipt(FundReceiptDTO dto) { + if (dto.getId() == null) throw new RuntimeException("收款ID不能为空"); + FundReceipt existing = receiptDataService.getById(dto.getId()); + if (existing == null || existing.getDeleted() == 1) throw new RuntimeException("收款记录不存在"); + if (existing.getReceiptStatus() == 2) throw new RuntimeException("已核销记录不允许修改"); + + FundReceipt receipt = new FundReceipt(); + receipt.setId(dto.getId()); + receipt.setTitle(dto.getTitle()); + receipt.setAmount(dto.getAmount()); + receipt.setPurpose(dto.getPurpose()); + receipt.setVoucher(dto.getVoucher()); + receipt.setAttachments(dto.getAttachments()); + receipt.setUpdatedTime(LocalDateTime.now()); + return receiptDataService.updateById(receipt); + } + + @Override + public FundReceiptVO getReceiptById(Long id) { + FundReceipt receipt = receiptDataService.getById(id); + if (receipt == null || receipt.getDeleted() == 1) throw new RuntimeException("收款记录不存在"); + return convertToVO(receipt); + } + + @Override + public Page pageReceipts(int pageNum, int pageSize, String title, Integer receiptType, Integer receiptStatus) { + Page page = new Page<>(pageNum, pageSize); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(FundReceipt::getDeleted, 0); + if (StringUtils.hasText(title)) wrapper.like(FundReceipt::getTitle, title); + if (receiptType != null) wrapper.eq(FundReceipt::getReceiptType, receiptType); + if (receiptStatus != null) wrapper.eq(FundReceipt::getReceiptStatus, receiptStatus); + wrapper.orderByDesc(FundReceipt::getCreatedTime); + + Page receiptPage = receiptDataService.page(page, wrapper); + Page voPage = new Page<>(receiptPage.getCurrent(), receiptPage.getSize(), receiptPage.getTotal()); + voPage.setRecords(receiptPage.getRecords().stream().map(this::convertToVO).toList()); + return voPage; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteReceipt(Long id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundReceipt::getId, id).set(FundReceipt::getDeleted, 1).set(FundReceipt::getUpdatedTime, LocalDateTime.now()); + return receiptDataService.update(wrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean confirm(Long id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundReceipt::getId, id).set(FundReceipt::getReceiptStatus, 1) + .set(FundReceipt::getConfirmTime, LocalDateTime.now()) + .set(FundReceipt::getUpdatedTime, LocalDateTime.now()); + return receiptDataService.update(wrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean writeOff(Long id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundReceipt::getId, id).set(FundReceipt::getReceiptStatus, 2) + .set(FundReceipt::getWriteOffTime, LocalDateTime.now()) + .set(FundReceipt::getUpdatedTime, LocalDateTime.now()); + return receiptDataService.update(wrapper); + } + + private String generateReceiptNo() { + String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + int seq = counter.getAndIncrement(); + return String.format("REC%s%04d", dateStr, seq); + } + + private FundReceiptVO convertToVO(FundReceipt r) { + FundReceiptVO vo = new FundReceiptVO(); + vo.setId(r.getId()); + vo.setReceiptNo(r.getReceiptNo()); + vo.setTitle(r.getTitle()); + vo.setAmount(r.getAmount()); + vo.setCurrency(r.getCurrency()); + vo.setReceiptType(r.getReceiptType()); + vo.setReceiptTypeName(getReceiptTypeName(r.getReceiptType())); + vo.setPayerName(r.getPayerName()); + vo.setPayerBank(r.getPayerBank()); + vo.setPayerAccount(r.getPayerAccount()); + vo.setReceiptDate(r.getReceiptDate()); + vo.setPurpose(r.getPurpose()); + vo.setProjectId(r.getProjectId()); + vo.setCustomerId(r.getCustomerId()); + vo.setReceiptStatus(r.getReceiptStatus()); + vo.setReceiptStatusName(getReceiptStatusName(r.getReceiptStatus())); + vo.setConfirmTime(r.getConfirmTime()); + vo.setConfirmBy(r.getConfirmBy()); + vo.setWriteOffTime(r.getWriteOffTime()); + vo.setWriteOffBy(r.getWriteOffBy()); + vo.setVoucher(r.getVoucher()); + vo.setInvoiceNo(r.getInvoiceNo()); + vo.setAttachments(r.getAttachments()); + vo.setTenantId(r.getTenantId()); + vo.setCreatedBy(r.getCreatedBy()); + vo.setCreatedTime(r.getCreatedTime()); + return vo; + } + + private String getReceiptTypeName(Integer type) { + if (type == null) return ""; + return switch (type) { case 1 -> "项目收款"; case 2 -> "服务费"; case 3 -> "利息收入"; case 4 -> "其他"; default -> ""; }; + } + + private String getReceiptStatusName(Integer status) { + if (status == null) return ""; + return switch (status) { case 0 -> "待确认"; case 1 -> "已确认"; case 2 -> "已核销"; default -> ""; }; + } +} diff --git a/fund-receipt/src/main/java/com/fundplatform/receipt/vo/FundReceiptVO.java b/fund-receipt/src/main/java/com/fundplatform/receipt/vo/FundReceiptVO.java new file mode 100644 index 0000000..fb5346d --- /dev/null +++ b/fund-receipt/src/main/java/com/fundplatform/receipt/vo/FundReceiptVO.java @@ -0,0 +1,87 @@ +package com.fundplatform.receipt.vo; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public class FundReceiptVO { + + private Long id; + private String receiptNo; + private String title; + private BigDecimal amount; + private String currency; + private Integer receiptType; + private String receiptTypeName; + private String payerName; + private String payerBank; + private String payerAccount; + private LocalDateTime receiptDate; + private String purpose; + private Long projectId; + private Long customerId; + private Integer receiptStatus; + private String receiptStatusName; + private LocalDateTime confirmTime; + private Long confirmBy; + private LocalDateTime writeOffTime; + private Long writeOffBy; + private String voucher; + private String invoiceNo; + private String attachments; + private Long tenantId; + private Long createdBy; + private LocalDateTime createdTime; + + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + public String getReceiptNo() { return receiptNo; } + public void setReceiptNo(String receiptNo) { this.receiptNo = receiptNo; } + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + public BigDecimal getAmount() { return amount; } + public void setAmount(BigDecimal amount) { this.amount = amount; } + public String getCurrency() { return currency; } + public void setCurrency(String currency) { this.currency = currency; } + public Integer getReceiptType() { return receiptType; } + public void setReceiptType(Integer receiptType) { this.receiptType = receiptType; } + public String getReceiptTypeName() { return receiptTypeName; } + public void setReceiptTypeName(String receiptTypeName) { this.receiptTypeName = receiptTypeName; } + public String getPayerName() { return payerName; } + public void setPayerName(String payerName) { this.payerName = payerName; } + public String getPayerBank() { return payerBank; } + public void setPayerBank(String payerBank) { this.payerBank = payerBank; } + public String getPayerAccount() { return payerAccount; } + public void setPayerAccount(String payerAccount) { this.payerAccount = payerAccount; } + public LocalDateTime getReceiptDate() { return receiptDate; } + public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; } + public String getPurpose() { return purpose; } + public void setPurpose(String purpose) { this.purpose = purpose; } + public Long getProjectId() { return projectId; } + public void setProjectId(Long projectId) { this.projectId = projectId; } + public Long getCustomerId() { return customerId; } + public void setCustomerId(Long customerId) { this.customerId = customerId; } + public Integer getReceiptStatus() { return receiptStatus; } + public void setReceiptStatus(Integer receiptStatus) { this.receiptStatus = receiptStatus; } + public String getReceiptStatusName() { return receiptStatusName; } + public void setReceiptStatusName(String receiptStatusName) { this.receiptStatusName = receiptStatusName; } + public LocalDateTime getConfirmTime() { return confirmTime; } + public void setConfirmTime(LocalDateTime confirmTime) { this.confirmTime = confirmTime; } + public Long getConfirmBy() { return confirmBy; } + public void setConfirmBy(Long confirmBy) { this.confirmBy = confirmBy; } + public LocalDateTime getWriteOffTime() { return writeOffTime; } + public void setWriteOffTime(LocalDateTime writeOffTime) { this.writeOffTime = writeOffTime; } + public Long getWriteOffBy() { return writeOffBy; } + public void setWriteOffBy(Long writeOffBy) { this.writeOffBy = writeOffBy; } + public String getVoucher() { return voucher; } + public void setVoucher(String voucher) { this.voucher = voucher; } + public String getInvoiceNo() { return invoiceNo; } + public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; } + public String getAttachments() { return attachments; } + public void setAttachments(String attachments) { this.attachments = attachments; } + public Long getTenantId() { return tenantId; } + public void setTenantId(Long tenantId) { this.tenantId = tenantId; } + public Long getCreatedBy() { return createdBy; } + public void setCreatedBy(Long createdBy) { this.createdBy = createdBy; } + public LocalDateTime getCreatedTime() { return createdTime; } + public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; } +} diff --git a/fund-receipt/src/main/resources/application.yml b/fund-receipt/src/main/resources/application.yml index db63eb3..2edc4f6 100644 --- a/fund-receipt/src/main/resources/application.yml +++ b/fund-receipt/src/main/resources/application.yml @@ -4,3 +4,10 @@ server: spring: application: name: fund-receipt + + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: fund-platform + group: DEFAULT_GROUP diff --git a/fund-report/src/main/java/com/fundplatform/report/ReportApplication.java b/fund-report/src/main/java/com/fundplatform/report/ReportApplication.java index 5262821..640a3ee 100644 --- a/fund-report/src/main/java/com/fundplatform/report/ReportApplication.java +++ b/fund-report/src/main/java/com/fundplatform/report/ReportApplication.java @@ -2,8 +2,10 @@ package com.fundplatform.report; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication +@EnableDiscoveryClient public class ReportApplication { public static void main(String[] args) { diff --git a/fund-report/src/main/resources/application.yml b/fund-report/src/main/resources/application.yml index 1d56059..28e1941 100644 --- a/fund-report/src/main/resources/application.yml +++ b/fund-report/src/main/resources/application.yml @@ -4,3 +4,10 @@ server: spring: application: name: fund-report + + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: fund-platform + group: DEFAULT_GROUP diff --git a/fund-req/src/main/java/com/fundplatform/req/ReqApplication.java b/fund-req/src/main/java/com/fundplatform/req/ReqApplication.java index be3b2ac..7ae26e7 100644 --- a/fund-req/src/main/java/com/fundplatform/req/ReqApplication.java +++ b/fund-req/src/main/java/com/fundplatform/req/ReqApplication.java @@ -2,8 +2,10 @@ package com.fundplatform.req; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication +@EnableDiscoveryClient public class ReqApplication { public static void main(String[] args) { diff --git a/fund-req/src/main/java/com/fundplatform/req/controller/FundRequestController.java b/fund-req/src/main/java/com/fundplatform/req/controller/FundRequestController.java new file mode 100644 index 0000000..79861ea --- /dev/null +++ b/fund-req/src/main/java/com/fundplatform/req/controller/FundRequestController.java @@ -0,0 +1,109 @@ +package com.fundplatform.req.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.common.core.Result; +import com.fundplatform.req.dto.FundRequestDTO; +import com.fundplatform.req.service.FundRequestService; +import com.fundplatform.req.vo.FundRequestVO; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +/** + * 用款申请Controller + */ +@RestController +@RequestMapping("/api/v1/req/fund-request") +public class FundRequestController { + + private final FundRequestService fundRequestService; + + public FundRequestController(FundRequestService fundRequestService) { + this.fundRequestService = fundRequestService; + } + + /** + * 创建用款申请 + */ + @PostMapping + public Result create(@Valid @RequestBody FundRequestDTO dto) { + Long id = fundRequestService.createRequest(dto); + return Result.success(id); + } + + /** + * 更新用款申请 + */ + @PutMapping + public Result update(@Valid @RequestBody FundRequestDTO dto) { + boolean result = fundRequestService.updateRequest(dto); + return Result.success(result); + } + + /** + * 根据ID查询用款申请 + */ + @GetMapping("/{id}") + public Result getById(@PathVariable Long id) { + FundRequestVO vo = fundRequestService.getRequestById(id); + return Result.success(vo); + } + + /** + * 分页查询用款申请 + */ + @GetMapping("/page") + public Result> page( + @RequestParam(defaultValue = "1") int pageNum, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) String title, + @RequestParam(required = false) Integer requestType, + @RequestParam(required = false) Integer approvalStatus) { + Page page = fundRequestService.pageRequests(pageNum, pageSize, title, requestType, approvalStatus); + return Result.success(page); + } + + /** + * 删除用款申请 + */ + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + boolean result = fundRequestService.deleteRequest(id); + return Result.success(result); + } + + /** + * 提交审批 + */ + @PostMapping("/{id}/submit") + public Result submit(@PathVariable Long id) { + boolean result = fundRequestService.submitApproval(id); + return Result.success(result); + } + + /** + * 审批通过 + */ + @PutMapping("/{id}/approve") + public Result approve(@PathVariable Long id, @RequestParam(required = false) String comment) { + boolean result = fundRequestService.approve(id, comment); + return Result.success(result); + } + + /** + * 审批拒绝 + */ + @PutMapping("/{id}/reject") + public Result reject(@PathVariable Long id, @RequestParam(required = false) String comment) { + boolean result = fundRequestService.reject(id, comment); + return Result.success(result); + } + + /** + * 撤回申请 + */ + @PutMapping("/{id}/withdraw") + public Result withdraw(@PathVariable Long id) { + boolean result = fundRequestService.withdraw(id); + return Result.success(result); + } +} diff --git a/fund-req/src/main/java/com/fundplatform/req/data/entity/FundRequest.java b/fund-req/src/main/java/com/fundplatform/req/data/entity/FundRequest.java new file mode 100644 index 0000000..7a7ec10 --- /dev/null +++ b/fund-req/src/main/java/com/fundplatform/req/data/entity/FundRequest.java @@ -0,0 +1,223 @@ +package com.fundplatform.req.data.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fundplatform.common.core.BaseEntity; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 用款申请实体 + */ +@TableName("fund_request") +public class FundRequest extends BaseEntity { + + /** 申请单号 */ + private String requestNo; + + /** 申请标题 */ + private String title; + + /** 申请金额 */ + private BigDecimal amount; + + /** 币种 */ + private String currency; + + /** 用款类型(1-日常报销 2-项目付款 3-预付款 4-其他) */ + private Integer requestType; + + /** 收款单位 */ + private String payeeName; + + /** 收款银行 */ + private String payeeBank; + + /** 收款账号 */ + private String payeeAccount; + + /** 用途说明 */ + private String purpose; + + /** 项目ID */ + private Long projectId; + + /** 客户ID */ + private Long customerId; + + /** 申请日期 */ + private LocalDateTime requestDate; + + /** 期望付款日期 */ + private LocalDateTime expectedPayDate; + + /** 审批状态(0-待审批 1-审批中 2-审批通过 3-审批拒绝 4-已撤回) */ + private Integer approvalStatus; + + /** 当前审批节点 */ + private Integer currentNode; + + /** 审批人ID */ + private Long approverId; + + /** 审批时间 */ + private LocalDateTime approvalTime; + + /** 审批意见 */ + private String approvalComment; + + /** 附件URL */ + private String attachments; + + public String getRequestNo() { + return requestNo; + } + + public void setRequestNo(String requestNo) { + this.requestNo = requestNo; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public Integer getRequestType() { + return requestType; + } + + public void setRequestType(Integer requestType) { + this.requestType = requestType; + } + + public String getPayeeName() { + return payeeName; + } + + public void setPayeeName(String payeeName) { + this.payeeName = payeeName; + } + + public String getPayeeBank() { + return payeeBank; + } + + public void setPayeeBank(String payeeBank) { + this.payeeBank = payeeBank; + } + + public String getPayeeAccount() { + return payeeAccount; + } + + public void setPayeeAccount(String payeeAccount) { + this.payeeAccount = payeeAccount; + } + + public String getPurpose() { + return purpose; + } + + public void setPurpose(String purpose) { + this.purpose = purpose; + } + + public Long getProjectId() { + return projectId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(Long customerId) { + this.customerId = customerId; + } + + public LocalDateTime getRequestDate() { + return requestDate; + } + + public void setRequestDate(LocalDateTime requestDate) { + this.requestDate = requestDate; + } + + public LocalDateTime getExpectedPayDate() { + return expectedPayDate; + } + + public void setExpectedPayDate(LocalDateTime expectedPayDate) { + this.expectedPayDate = expectedPayDate; + } + + public Integer getApprovalStatus() { + return approvalStatus; + } + + public void setApprovalStatus(Integer approvalStatus) { + this.approvalStatus = approvalStatus; + } + + public Integer getCurrentNode() { + return currentNode; + } + + public void setCurrentNode(Integer currentNode) { + this.currentNode = currentNode; + } + + public Long getApproverId() { + return approverId; + } + + public void setApproverId(Long approverId) { + this.approverId = approverId; + } + + public LocalDateTime getApprovalTime() { + return approvalTime; + } + + public void setApprovalTime(LocalDateTime approvalTime) { + this.approvalTime = approvalTime; + } + + public String getApprovalComment() { + return approvalComment; + } + + public void setApprovalComment(String approvalComment) { + this.approvalComment = approvalComment; + } + + public String getAttachments() { + return attachments; + } + + public void setAttachments(String attachments) { + this.attachments = attachments; + } +} diff --git a/fund-req/src/main/java/com/fundplatform/req/data/mapper/FundRequestMapper.java b/fund-req/src/main/java/com/fundplatform/req/data/mapper/FundRequestMapper.java new file mode 100644 index 0000000..e1f7045 --- /dev/null +++ b/fund-req/src/main/java/com/fundplatform/req/data/mapper/FundRequestMapper.java @@ -0,0 +1,12 @@ +package com.fundplatform.req.data.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fundplatform.req.data.entity.FundRequest; +import org.apache.ibatis.annotations.Mapper; + +/** + * 用款申请Mapper + */ +@Mapper +public interface FundRequestMapper extends BaseMapper { +} diff --git a/fund-req/src/main/java/com/fundplatform/req/data/service/FundRequestDataService.java b/fund-req/src/main/java/com/fundplatform/req/data/service/FundRequestDataService.java new file mode 100644 index 0000000..03929cb --- /dev/null +++ b/fund-req/src/main/java/com/fundplatform/req/data/service/FundRequestDataService.java @@ -0,0 +1,15 @@ +package com.fundplatform.req.data.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fundplatform.req.data.entity.FundRequest; +import com.fundplatform.req.data.mapper.FundRequestMapper; +import org.springframework.stereotype.Service; + +/** + * 用款申请数据服务 + * 职责:仅负责数据访问、数据封装和转换,不承载业务逻辑 + */ +@Service +public class FundRequestDataService extends ServiceImpl implements IService { +} diff --git a/fund-req/src/main/java/com/fundplatform/req/dto/FundRequestDTO.java b/fund-req/src/main/java/com/fundplatform/req/dto/FundRequestDTO.java new file mode 100644 index 0000000..3348568 --- /dev/null +++ b/fund-req/src/main/java/com/fundplatform/req/dto/FundRequestDTO.java @@ -0,0 +1,165 @@ +package com.fundplatform.req.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 用款申请DTO + */ +public class FundRequestDTO { + + private Long id; + + @NotBlank(message = "申请标题不能为空") + @Size(max = 200, message = "申请标题不能超过200个字符") + private String title; + + @NotNull(message = "申请金额不能为空") + @Positive(message = "申请金额必须大于0") + private BigDecimal amount; + + private String currency = "CNY"; + + @NotNull(message = "用款类型不能为空") + private Integer requestType; + + @NotBlank(message = "收款单位不能为空") + @Size(max = 100, message = "收款单位不能超过100个字符") + private String payeeName; + + @Size(max = 100, message = "收款银行不能超过100个字符") + private String payeeBank; + + @Size(max = 50, message = "收款账号不能超过50个字符") + private String payeeAccount; + + @Size(max = 500, message = "用途说明不能超过500个字符") + private String purpose; + + private Long projectId; + + private Long customerId; + + private LocalDateTime expectedPayDate; + + private String attachments; + + private String remark; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public Integer getRequestType() { + return requestType; + } + + public void setRequestType(Integer requestType) { + this.requestType = requestType; + } + + public String getPayeeName() { + return payeeName; + } + + public void setPayeeName(String payeeName) { + this.payeeName = payeeName; + } + + public String getPayeeBank() { + return payeeBank; + } + + public void setPayeeBank(String payeeBank) { + this.payeeBank = payeeBank; + } + + public String getPayeeAccount() { + return payeeAccount; + } + + public void setPayeeAccount(String payeeAccount) { + this.payeeAccount = payeeAccount; + } + + public String getPurpose() { + return purpose; + } + + public void setPurpose(String purpose) { + this.purpose = purpose; + } + + public Long getProjectId() { + return projectId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(Long customerId) { + this.customerId = customerId; + } + + public LocalDateTime getExpectedPayDate() { + return expectedPayDate; + } + + public void setExpectedPayDate(LocalDateTime expectedPayDate) { + this.expectedPayDate = expectedPayDate; + } + + public String getAttachments() { + return attachments; + } + + public void setAttachments(String attachments) { + this.attachments = attachments; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } +} diff --git a/fund-req/src/main/java/com/fundplatform/req/service/FundRequestService.java b/fund-req/src/main/java/com/fundplatform/req/service/FundRequestService.java new file mode 100644 index 0000000..a654a40 --- /dev/null +++ b/fund-req/src/main/java/com/fundplatform/req/service/FundRequestService.java @@ -0,0 +1,89 @@ +package com.fundplatform.req.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.req.dto.FundRequestDTO; +import com.fundplatform.req.vo.FundRequestVO; + +/** + * 用款申请服务接口 + */ +public interface FundRequestService { + + /** + * 创建用款申请 + * + * @param dto 用款申请DTO + * @return 申请ID + */ + Long createRequest(FundRequestDTO dto); + + /** + * 更新用款申请 + * + * @param dto 用款申请DTO + * @return 是否成功 + */ + boolean updateRequest(FundRequestDTO dto); + + /** + * 根据ID查询用款申请 + * + * @param id 申请ID + * @return 用款申请VO + */ + FundRequestVO getRequestById(Long id); + + /** + * 分页查询用款申请 + * + * @param pageNum 页码 + * @param pageSize 每页大小 + * @param title 标题(模糊查询) + * @param requestType 用款类型 + * @param approvalStatus 审批状态 + * @return 分页数据 + */ + Page pageRequests(int pageNum, int pageSize, String title, Integer requestType, Integer approvalStatus); + + /** + * 删除用款申请 + * + * @param id 申请ID + * @return 是否成功 + */ + boolean deleteRequest(Long id); + + /** + * 提交审批 + * + * @param id 申请ID + * @return 是否成功 + */ + boolean submitApproval(Long id); + + /** + * 审批通过 + * + * @param id 申请ID + * @param comment 审批意见 + * @return 是否成功 + */ + boolean approve(Long id, String comment); + + /** + * 审批拒绝 + * + * @param id 申请ID + * @param comment 审批意见 + * @return 是否成功 + */ + boolean reject(Long id, String comment); + + /** + * 撤回申请 + * + * @param id 申请ID + * @return 是否成功 + */ + boolean withdraw(Long id); +} diff --git a/fund-req/src/main/java/com/fundplatform/req/service/impl/FundRequestServiceImpl.java b/fund-req/src/main/java/com/fundplatform/req/service/impl/FundRequestServiceImpl.java new file mode 100644 index 0000000..6bd83e6 --- /dev/null +++ b/fund-req/src/main/java/com/fundplatform/req/service/impl/FundRequestServiceImpl.java @@ -0,0 +1,301 @@ +package com.fundplatform.req.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.req.data.entity.FundRequest; +import com.fundplatform.req.data.service.FundRequestDataService; +import com.fundplatform.req.dto.FundRequestDTO; +import com.fundplatform.req.service.FundRequestService; +import com.fundplatform.req.vo.FundRequestVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 用款申请服务实现 + */ +@Service +public class FundRequestServiceImpl implements FundRequestService { + + private static final Logger log = LoggerFactory.getLogger(FundRequestServiceImpl.class); + private static final AtomicInteger counter = new AtomicInteger(1); + + private final FundRequestDataService requestDataService; + + public FundRequestServiceImpl(FundRequestDataService requestDataService) { + this.requestDataService = requestDataService; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createRequest(FundRequestDTO dto) { + FundRequest request = new FundRequest(); + request.setRequestNo(generateRequestNo()); + request.setTitle(dto.getTitle()); + request.setAmount(dto.getAmount()); + request.setCurrency(dto.getCurrency() != null ? dto.getCurrency() : "CNY"); + request.setRequestType(dto.getRequestType()); + request.setPayeeName(dto.getPayeeName()); + request.setPayeeBank(dto.getPayeeBank()); + request.setPayeeAccount(dto.getPayeeAccount()); + request.setPurpose(dto.getPurpose()); + request.setProjectId(dto.getProjectId()); + request.setCustomerId(dto.getCustomerId()); + request.setRequestDate(LocalDateTime.now()); + request.setExpectedPayDate(dto.getExpectedPayDate()); + request.setApprovalStatus(0); // 待审批 + request.setCurrentNode(1); + request.setAttachments(dto.getAttachments()); + request.setDeleted(0); + request.setCreatedTime(LocalDateTime.now()); + + requestDataService.save(request); + log.info("创建用款申请成功: id={}, requestNo={}", request.getId(), request.getRequestNo()); + return request.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateRequest(FundRequestDTO dto) { + if (dto.getId() == null) { + throw new RuntimeException("申请ID不能为空"); + } + + FundRequest existing = requestDataService.getById(dto.getId()); + if (existing == null || existing.getDeleted() == 1) { + throw new RuntimeException("用款申请不存在"); + } + + // 只有待审批状态才能修改 + if (existing.getApprovalStatus() != 0) { + throw new RuntimeException("当前状态不允许修改"); + } + + FundRequest request = new FundRequest(); + request.setId(dto.getId()); + request.setTitle(dto.getTitle()); + request.setAmount(dto.getAmount()); + request.setCurrency(dto.getCurrency()); + request.setRequestType(dto.getRequestType()); + request.setPayeeName(dto.getPayeeName()); + request.setPayeeBank(dto.getPayeeBank()); + request.setPayeeAccount(dto.getPayeeAccount()); + request.setPurpose(dto.getPurpose()); + request.setProjectId(dto.getProjectId()); + request.setCustomerId(dto.getCustomerId()); + request.setExpectedPayDate(dto.getExpectedPayDate()); + request.setAttachments(dto.getAttachments()); + request.setUpdatedTime(LocalDateTime.now()); + + boolean result = requestDataService.updateById(request); + log.info("更新用款申请: id={}, result={}", dto.getId(), result); + return result; + } + + @Override + public FundRequestVO getRequestById(Long id) { + FundRequest request = requestDataService.getById(id); + if (request == null || request.getDeleted() == 1) { + throw new RuntimeException("用款申请不存在"); + } + return convertToVO(request); + } + + @Override + public Page pageRequests(int pageNum, int pageSize, String title, Integer requestType, Integer approvalStatus) { + Page page = new Page<>(pageNum, pageSize); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(FundRequest::getDeleted, 0); + + if (StringUtils.hasText(title)) { + wrapper.like(FundRequest::getTitle, title); + } + if (requestType != null) { + wrapper.eq(FundRequest::getRequestType, requestType); + } + if (approvalStatus != null) { + wrapper.eq(FundRequest::getApprovalStatus, approvalStatus); + } + wrapper.orderByDesc(FundRequest::getCreatedTime); + + Page requestPage = requestDataService.page(page, wrapper); + + Page voPage = new Page<>(requestPage.getCurrent(), requestPage.getSize(), requestPage.getTotal()); + voPage.setRecords(requestPage.getRecords().stream().map(this::convertToVO).toList()); + return voPage; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteRequest(Long id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundRequest::getId, id); + wrapper.set(FundRequest::getDeleted, 1); + wrapper.set(FundRequest::getUpdatedTime, LocalDateTime.now()); + boolean result = requestDataService.update(wrapper); + log.info("删除用款申请: id={}", id); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean submitApproval(Long id) { + FundRequest request = requestDataService.getById(id); + if (request == null || request.getDeleted() == 1) { + throw new RuntimeException("用款申请不存在"); + } + + if (request.getApprovalStatus() != 0) { + throw new RuntimeException("当前状态不允许提交审批"); + } + + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundRequest::getId, id); + wrapper.set(FundRequest::getApprovalStatus, 1); // 审批中 + wrapper.set(FundRequest::getUpdatedTime, LocalDateTime.now()); + boolean result = requestDataService.update(wrapper); + log.info("提交用款申请审批: id={}", id); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean approve(Long id, String comment) { + FundRequest request = requestDataService.getById(id); + if (request == null || request.getDeleted() == 1) { + throw new RuntimeException("用款申请不存在"); + } + + if (request.getApprovalStatus() != 1) { + throw new RuntimeException("当前状态不允许审批"); + } + + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundRequest::getId, id); + wrapper.set(FundRequest::getApprovalStatus, 2); // 审批通过 + wrapper.set(FundRequest::getApprovalTime, LocalDateTime.now()); + wrapper.set(FundRequest::getApprovalComment, comment); + wrapper.set(FundRequest::getUpdatedTime, LocalDateTime.now()); + boolean result = requestDataService.update(wrapper); + log.info("审批通过用款申请: id={}", id); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean reject(Long id, String comment) { + FundRequest request = requestDataService.getById(id); + if (request == null || request.getDeleted() == 1) { + throw new RuntimeException("用款申请不存在"); + } + + if (request.getApprovalStatus() != 1) { + throw new RuntimeException("当前状态不允许审批"); + } + + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundRequest::getId, id); + wrapper.set(FundRequest::getApprovalStatus, 3); // 审批拒绝 + wrapper.set(FundRequest::getApprovalTime, LocalDateTime.now()); + wrapper.set(FundRequest::getApprovalComment, comment); + wrapper.set(FundRequest::getUpdatedTime, LocalDateTime.now()); + boolean result = requestDataService.update(wrapper); + log.info("审批拒绝用款申请: id={}", id); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean withdraw(Long id) { + FundRequest request = requestDataService.getById(id); + if (request == null || request.getDeleted() == 1) { + throw new RuntimeException("用款申请不存在"); + } + + if (request.getApprovalStatus() != 1) { + throw new RuntimeException("当前状态不允许撤回"); + } + + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(FundRequest::getId, id); + wrapper.set(FundRequest::getApprovalStatus, 4); // 已撤回 + wrapper.set(FundRequest::getUpdatedTime, LocalDateTime.now()); + boolean result = requestDataService.update(wrapper); + log.info("撤回用款申请: id={}", id); + return result; + } + + /** + * 生成申请单号 + */ + private String generateRequestNo() { + String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + int seq = counter.getAndIncrement(); + return String.format("REQ%s%04d", dateStr, seq); + } + + /** + * 转换为VO + */ + private FundRequestVO convertToVO(FundRequest request) { + FundRequestVO vo = new FundRequestVO(); + vo.setId(request.getId()); + vo.setRequestNo(request.getRequestNo()); + vo.setTitle(request.getTitle()); + vo.setAmount(request.getAmount()); + vo.setCurrency(request.getCurrency()); + vo.setRequestType(request.getRequestType()); + vo.setRequestTypeName(getRequestTypeName(request.getRequestType())); + vo.setPayeeName(request.getPayeeName()); + vo.setPayeeBank(request.getPayeeBank()); + vo.setPayeeAccount(request.getPayeeAccount()); + vo.setPurpose(request.getPurpose()); + vo.setProjectId(request.getProjectId()); + vo.setCustomerId(request.getCustomerId()); + vo.setRequestDate(request.getRequestDate()); + vo.setExpectedPayDate(request.getExpectedPayDate()); + vo.setApprovalStatus(request.getApprovalStatus()); + vo.setApprovalStatusName(getApprovalStatusName(request.getApprovalStatus())); + vo.setCurrentNode(request.getCurrentNode()); + vo.setApproverId(request.getApproverId()); + vo.setApprovalTime(request.getApprovalTime()); + vo.setApprovalComment(request.getApprovalComment()); + vo.setAttachments(request.getAttachments()); + vo.setTenantId(request.getTenantId()); + vo.setCreatedBy(request.getCreatedBy()); + vo.setCreatedTime(request.getCreatedTime()); + vo.setUpdatedTime(request.getUpdatedTime()); + return vo; + } + + private String getRequestTypeName(Integer type) { + if (type == null) return ""; + switch (type) { + case 1: return "日常报销"; + case 2: return "项目付款"; + case 3: return "预付款"; + case 4: return "其他"; + default: return ""; + } + } + + private String getApprovalStatusName(Integer status) { + if (status == null) return ""; + switch (status) { + case 0: return "待审批"; + case 1: return "审批中"; + case 2: return "审批通过"; + case 3: return "审批拒绝"; + case 4: return "已撤回"; + default: return ""; + } + } +} diff --git a/fund-req/src/main/java/com/fundplatform/req/vo/FundRequestVO.java b/fund-req/src/main/java/com/fundplatform/req/vo/FundRequestVO.java new file mode 100644 index 0000000..4756319 --- /dev/null +++ b/fund-req/src/main/java/com/fundplatform/req/vo/FundRequestVO.java @@ -0,0 +1,281 @@ +package com.fundplatform.req.vo; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 用款申请VO + */ +public class FundRequestVO { + + private Long id; + private String requestNo; + private String title; + private BigDecimal amount; + private String currency; + private Integer requestType; + private String requestTypeName; + private String payeeName; + private String payeeBank; + private String payeeAccount; + private String purpose; + private Long projectId; + private String projectName; + private Long customerId; + private String customerName; + private LocalDateTime requestDate; + private LocalDateTime expectedPayDate; + private Integer approvalStatus; + private String approvalStatusName; + private Integer currentNode; + private Long approverId; + private String approverName; + private LocalDateTime approvalTime; + private String approvalComment; + private String attachments; + private Long tenantId; + private Long createdBy; + private String createdByName; + private LocalDateTime createdTime; + private LocalDateTime updatedTime; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getRequestNo() { + return requestNo; + } + + public void setRequestNo(String requestNo) { + this.requestNo = requestNo; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public Integer getRequestType() { + return requestType; + } + + public void setRequestType(Integer requestType) { + this.requestType = requestType; + } + + public String getRequestTypeName() { + return requestTypeName; + } + + public void setRequestTypeName(String requestTypeName) { + this.requestTypeName = requestTypeName; + } + + public String getPayeeName() { + return payeeName; + } + + public void setPayeeName(String payeeName) { + this.payeeName = payeeName; + } + + public String getPayeeBank() { + return payeeBank; + } + + public void setPayeeBank(String payeeBank) { + this.payeeBank = payeeBank; + } + + public String getPayeeAccount() { + return payeeAccount; + } + + public void setPayeeAccount(String payeeAccount) { + this.payeeAccount = payeeAccount; + } + + public String getPurpose() { + return purpose; + } + + public void setPurpose(String purpose) { + this.purpose = purpose; + } + + public Long getProjectId() { + return projectId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public String getProjectName() { + return projectName; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(Long customerId) { + this.customerId = customerId; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public LocalDateTime getRequestDate() { + return requestDate; + } + + public void setRequestDate(LocalDateTime requestDate) { + this.requestDate = requestDate; + } + + public LocalDateTime getExpectedPayDate() { + return expectedPayDate; + } + + public void setExpectedPayDate(LocalDateTime expectedPayDate) { + this.expectedPayDate = expectedPayDate; + } + + public Integer getApprovalStatus() { + return approvalStatus; + } + + public void setApprovalStatus(Integer approvalStatus) { + this.approvalStatus = approvalStatus; + } + + public String getApprovalStatusName() { + return approvalStatusName; + } + + public void setApprovalStatusName(String approvalStatusName) { + this.approvalStatusName = approvalStatusName; + } + + public Integer getCurrentNode() { + return currentNode; + } + + public void setCurrentNode(Integer currentNode) { + this.currentNode = currentNode; + } + + public Long getApproverId() { + return approverId; + } + + public void setApproverId(Long approverId) { + this.approverId = approverId; + } + + public String getApproverName() { + return approverName; + } + + public void setApproverName(String approverName) { + this.approverName = approverName; + } + + public LocalDateTime getApprovalTime() { + return approvalTime; + } + + public void setApprovalTime(LocalDateTime approvalTime) { + this.approvalTime = approvalTime; + } + + public String getApprovalComment() { + return approvalComment; + } + + public void setApprovalComment(String approvalComment) { + this.approvalComment = approvalComment; + } + + public String getAttachments() { + return attachments; + } + + public void setAttachments(String attachments) { + this.attachments = attachments; + } + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + public Long getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(Long createdBy) { + this.createdBy = createdBy; + } + + public String getCreatedByName() { + return createdByName; + } + + public void setCreatedByName(String createdByName) { + this.createdByName = createdByName; + } + + public LocalDateTime getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(LocalDateTime createdTime) { + this.createdTime = createdTime; + } + + public LocalDateTime getUpdatedTime() { + return updatedTime; + } + + public void setUpdatedTime(LocalDateTime updatedTime) { + this.updatedTime = updatedTime; + } +} diff --git a/fund-req/src/main/resources/application.yml b/fund-req/src/main/resources/application.yml index 2e6c5a9..a306b94 100644 --- a/fund-req/src/main/resources/application.yml +++ b/fund-req/src/main/resources/application.yml @@ -4,3 +4,10 @@ server: spring: application: name: fund-req + + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: fund-platform + group: DEFAULT_GROUP diff --git a/fund-sys/src/main/java/com/fundplatform/sys/SysApplication.java b/fund-sys/src/main/java/com/fundplatform/sys/SysApplication.java index 74611b6..5e18dfb 100644 --- a/fund-sys/src/main/java/com/fundplatform/sys/SysApplication.java +++ b/fund-sys/src/main/java/com/fundplatform/sys/SysApplication.java @@ -3,11 +3,13 @@ package com.fundplatform.sys; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * 系统服务启动类 */ @SpringBootApplication +@EnableDiscoveryClient @MapperScan("com.fundplatform.sys.data.mapper") public class SysApplication { diff --git a/fund-sys/src/main/java/com/fundplatform/sys/controller/DeptController.java b/fund-sys/src/main/java/com/fundplatform/sys/controller/DeptController.java new file mode 100644 index 0000000..6396f48 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/controller/DeptController.java @@ -0,0 +1,66 @@ +package com.fundplatform.sys.controller; + +import com.fundplatform.common.core.Result; +import com.fundplatform.sys.dto.DeptDTO; +import com.fundplatform.sys.service.DeptService; +import com.fundplatform.sys.vo.DeptVO; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 部门管理Controller + */ +@RestController +@RequestMapping("/api/v1/sys/dept") +public class DeptController { + + private final DeptService deptService; + + public DeptController(DeptService deptService) { + this.deptService = deptService; + } + + @PostMapping + public Result create(@Valid @RequestBody DeptDTO dto) { + Long deptId = deptService.createDept(dto); + return Result.success(deptId); + } + + @PutMapping + public Result update(@Valid @RequestBody DeptDTO dto) { + boolean result = deptService.updateDept(dto); + return Result.success(result); + } + + @GetMapping("/{id}") + public Result getById(@PathVariable Long id) { + DeptVO vo = deptService.getDeptById(id); + return Result.success(vo); + } + + @GetMapping("/tree") + public Result> getTree() { + List tree = deptService.getDeptTree(); + return Result.success(tree); + } + + @GetMapping("/list") + public Result> listAll() { + List list = deptService.listAllDepts(); + return Result.success(list); + } + + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + boolean result = deptService.deleteDept(id); + return Result.success(result); + } + + @PutMapping("/{id}/status") + public Result updateStatus(@PathVariable Long id, @RequestParam Integer status) { + boolean result = deptService.updateStatus(id, status); + return Result.success(result); + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/controller/MenuController.java b/fund-sys/src/main/java/com/fundplatform/sys/controller/MenuController.java new file mode 100644 index 0000000..7bb9ffe --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/controller/MenuController.java @@ -0,0 +1,60 @@ +package com.fundplatform.sys.controller; + +import com.fundplatform.common.core.Result; +import com.fundplatform.sys.dto.MenuDTO; +import com.fundplatform.sys.service.MenuService; +import com.fundplatform.sys.vo.MenuVO; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 菜单管理Controller + */ +@RestController +@RequestMapping("/api/v1/sys/menu") +public class MenuController { + + private final MenuService menuService; + + public MenuController(MenuService menuService) { + this.menuService = menuService; + } + + @PostMapping + public Result create(@Valid @RequestBody MenuDTO dto) { + Long menuId = menuService.createMenu(dto); + return Result.success(menuId); + } + + @PutMapping + public Result update(@Valid @RequestBody MenuDTO dto) { + boolean result = menuService.updateMenu(dto); + return Result.success(result); + } + + @GetMapping("/{id}") + public Result getById(@PathVariable Long id) { + MenuVO vo = menuService.getMenuById(id); + return Result.success(vo); + } + + @GetMapping("/tree") + public Result> getTree() { + List tree = menuService.getMenuTree(); + return Result.success(tree); + } + + @GetMapping("/user/{userId}") + public Result> getUserTree(@PathVariable Long userId) { + List tree = menuService.getUserMenuTree(userId); + return Result.success(tree); + } + + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + boolean result = menuService.deleteMenu(id); + return Result.success(result); + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/controller/RoleController.java b/fund-sys/src/main/java/com/fundplatform/sys/controller/RoleController.java new file mode 100644 index 0000000..5c009d8 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/controller/RoleController.java @@ -0,0 +1,77 @@ +package com.fundplatform.sys.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.common.core.Result; +import com.fundplatform.sys.dto.RoleDTO; +import com.fundplatform.sys.service.RoleService; +import com.fundplatform.sys.vo.RoleVO; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 角色管理Controller + */ +@RestController +@RequestMapping("/api/v1/sys/role") +public class RoleController { + + private final RoleService roleService; + + public RoleController(RoleService roleService) { + this.roleService = roleService; + } + + @PostMapping + public Result create(@Valid @RequestBody RoleDTO dto) { + Long roleId = roleService.createRole(dto); + return Result.success(roleId); + } + + @PutMapping + public Result update(@Valid @RequestBody RoleDTO dto) { + boolean result = roleService.updateRole(dto); + return Result.success(result); + } + + @GetMapping("/{id}") + public Result getById(@PathVariable Long id) { + RoleVO vo = roleService.getRoleById(id); + return Result.success(vo); + } + + @GetMapping("/page") + public Result> page( + @RequestParam(defaultValue = "1") int pageNum, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) String roleName, + @RequestParam(required = false) Integer status) { + Page page = roleService.pageRoles(pageNum, pageSize, roleName, status); + return Result.success(page); + } + + @GetMapping("/list") + public Result> listAll() { + List list = roleService.listAllRoles(); + return Result.success(list); + } + + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + boolean result = roleService.deleteRole(id); + return Result.success(result); + } + + @PutMapping("/{id}/status") + public Result updateStatus(@PathVariable Long id, @RequestParam Integer status) { + boolean result = roleService.updateStatus(id, status); + return Result.success(result); + } + + @PostMapping("/{id}/menus") + public Result assignMenus(@PathVariable Long id, @RequestBody List menuIds) { + boolean result = roleService.assignMenus(id, menuIds); + return Result.success(result); + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/controller/UserController.java b/fund-sys/src/main/java/com/fundplatform/sys/controller/UserController.java new file mode 100644 index 0000000..39cc545 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/controller/UserController.java @@ -0,0 +1,100 @@ +package com.fundplatform.sys.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.common.core.Result; +import com.fundplatform.sys.dto.UserDTO; +import com.fundplatform.sys.service.UserService; +import com.fundplatform.sys.vo.UserVO; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +/** + * 用户管理Controller + */ +@RestController +@RequestMapping("/api/v1/sys/user") +public class UserController { + + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + + /** + * 创建用户 + */ + @PostMapping + public Result create(@Valid @RequestBody UserDTO dto) { + Long userId = userService.createUser(dto); + return Result.success(userId); + } + + /** + * 更新用户 + */ + @PutMapping + public Result update(@Valid @RequestBody UserDTO dto) { + boolean result = userService.updateUser(dto); + return Result.success(result); + } + + /** + * 根据ID查询用户 + */ + @GetMapping("/{id}") + public Result getById(@PathVariable Long id) { + UserVO vo = userService.getUserById(id); + return Result.success(vo); + } + + /** + * 分页查询用户 + */ + @GetMapping("/page") + public Result> page( + @RequestParam(defaultValue = "1") int pageNum, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) String username, + @RequestParam(required = false) Integer status, + @RequestParam(required = false) Long deptId) { + Page page = userService.pageUsers(pageNum, pageSize, username, status, deptId); + return Result.success(page); + } + + /** + * 删除用户 + */ + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + boolean result = userService.deleteUser(id); + return Result.success(result); + } + + /** + * 批量删除用户 + */ + @DeleteMapping("/batch") + public Result batchDelete(@RequestBody Long[] ids) { + boolean result = userService.batchDeleteUsers(ids); + return Result.success(result); + } + + /** + * 重置密码 + */ + @PutMapping("/{id}/reset-password") + public Result resetPassword(@PathVariable Long id) { + boolean result = userService.resetPassword(id); + return Result.success(result); + } + + /** + * 更新用户状态 + */ + @PutMapping("/{id}/status") + public Result updateStatus(@PathVariable Long id, @RequestParam Integer status) { + boolean result = userService.updateStatus(id, status); + return Result.success(result); + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/dto/DeptDTO.java b/fund-sys/src/main/java/com/fundplatform/sys/dto/DeptDTO.java new file mode 100644 index 0000000..1bc3d24 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/dto/DeptDTO.java @@ -0,0 +1,115 @@ +package com.fundplatform.sys.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * 部门DTO + */ +public class DeptDTO { + + private Long id; + + private Long parentId; + + @NotBlank(message = "部门编码不能为空") + @Size(max = 50, message = "部门编码不能超过50个字符") + private String deptCode; + + @NotBlank(message = "部门名称不能为空") + @Size(max = 50, message = "部门名称不能超过50个字符") + private String deptName; + + @Size(max = 50, message = "部门负责人不能超过50个字符") + private String deptLeader; + + private String phone; + + private String email; + + private Integer sortOrder; + + private Integer status; + + private String remark; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getDeptCode() { + return deptCode; + } + + public void setDeptCode(String deptCode) { + this.deptCode = deptCode; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptLeader() { + return deptLeader; + } + + public void setDeptLeader(String deptLeader) { + this.deptLeader = deptLeader; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Integer getSortOrder() { + return sortOrder; + } + + public void setSortOrder(Integer sortOrder) { + this.sortOrder = sortOrder; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/dto/MenuDTO.java b/fund-sys/src/main/java/com/fundplatform/sys/dto/MenuDTO.java new file mode 100644 index 0000000..8d79d74 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/dto/MenuDTO.java @@ -0,0 +1,136 @@ +package com.fundplatform.sys.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * 菜单DTO + */ +public class MenuDTO { + + private Long id; + + private Long parentId; + + @NotBlank(message = "菜单名称不能为空") + @Size(max = 50, message = "菜单名称不能超过50个字符") + private String menuName; + + private Integer menuType; + + @Size(max = 200, message = "菜单路径不能超过200个字符") + private String menuPath; + + @Size(max = 100, message = "菜单图标不能超过100个字符") + private String menuIcon; + + @Size(max = 200, message = "组件路径不能超过200个字符") + private String component; + + @Size(max = 100, message = "权限标识不能超过100个字符") + private String permission; + + private Integer sortOrder; + + private Integer visible; + + private Integer status; + + private String remark; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getMenuName() { + return menuName; + } + + public void setMenuName(String menuName) { + this.menuName = menuName; + } + + public Integer getMenuType() { + return menuType; + } + + public void setMenuType(Integer menuType) { + this.menuType = menuType; + } + + public String getMenuPath() { + return menuPath; + } + + public void setMenuPath(String menuPath) { + this.menuPath = menuPath; + } + + public String getMenuIcon() { + return menuIcon; + } + + public void setMenuIcon(String menuIcon) { + this.menuIcon = menuIcon; + } + + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public Integer getSortOrder() { + return sortOrder; + } + + public void setSortOrder(Integer sortOrder) { + this.sortOrder = sortOrder; + } + + public Integer getVisible() { + return visible; + } + + public void setVisible(Integer visible) { + this.visible = visible; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/dto/RoleDTO.java b/fund-sys/src/main/java/com/fundplatform/sys/dto/RoleDTO.java new file mode 100644 index 0000000..9b5ae4b --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/dto/RoleDTO.java @@ -0,0 +1,84 @@ +package com.fundplatform.sys.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * 角色DTO + */ +public class RoleDTO { + + private Long id; + + @NotBlank(message = "角色编码不能为空") + @Size(max = 50, message = "角色编码不能超过50个字符") + private String roleCode; + + @NotBlank(message = "角色名称不能为空") + @Size(max = 50, message = "角色名称不能超过50个字符") + private String roleName; + + private Integer dataScope; + + private Integer status; + + private Integer sortOrder; + + private String remark; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getRoleCode() { + return roleCode; + } + + public void setRoleCode(String roleCode) { + this.roleCode = roleCode; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public Integer getDataScope() { + return dataScope; + } + + public void setDataScope(Integer dataScope) { + this.dataScope = dataScope; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getSortOrder() { + return sortOrder; + } + + public void setSortOrder(Integer sortOrder) { + this.sortOrder = sortOrder; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/dto/UserDTO.java b/fund-sys/src/main/java/com/fundplatform/sys/dto/UserDTO.java new file mode 100644 index 0000000..1b3494d --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/dto/UserDTO.java @@ -0,0 +1,118 @@ +package com.fundplatform.sys.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +/** + * 用户DTO(创建/更新) + */ +public class UserDTO { + + private Long id; + + @NotBlank(message = "用户名不能为空") + @Size(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间") + @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线") + private String username; + + @Size(min = 6, max = 100, message = "密码长度必须在6-100个字符之间") + private String password; + + @Size(max = 50, message = "真实姓名不能超过50个字符") + private String realName; + + @Pattern(regexp = "^$|^1[3-9]\\d{9}$", message = "手机号格式不正确") + private String phone; + + @Pattern(regexp = "^$|^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", message = "邮箱格式不正确") + private String email; + + private Long deptId; + + private Integer status; + + private String avatar; + + private String remark; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getRealName() { + return realName; + } + + public void setRealName(String realName) { + this.realName = realName; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Long getDeptId() { + return deptId; + } + + public void setDeptId(Long deptId) { + this.deptId = deptId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/service/DeptService.java b/fund-sys/src/main/java/com/fundplatform/sys/service/DeptService.java new file mode 100644 index 0000000..40c87d7 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/service/DeptService.java @@ -0,0 +1,26 @@ +package com.fundplatform.sys.service; + +import com.fundplatform.sys.dto.DeptDTO; +import com.fundplatform.sys.vo.DeptVO; + +import java.util.List; + +/** + * 部门服务接口 + */ +public interface DeptService { + + Long createDept(DeptDTO dto); + + boolean updateDept(DeptDTO dto); + + DeptVO getDeptById(Long id); + + List getDeptTree(); + + List listAllDepts(); + + boolean deleteDept(Long id); + + boolean updateStatus(Long id, Integer status); +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/service/MenuService.java b/fund-sys/src/main/java/com/fundplatform/sys/service/MenuService.java new file mode 100644 index 0000000..7f1eec8 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/service/MenuService.java @@ -0,0 +1,42 @@ +package com.fundplatform.sys.service; + +import com.fundplatform.sys.dto.MenuDTO; +import com.fundplatform.sys.vo.MenuVO; + +import java.util.List; + +/** + * 菜单服务接口 + */ +public interface MenuService { + + /** + * 创建菜单 + */ + Long createMenu(MenuDTO dto); + + /** + * 更新菜单 + */ + boolean updateMenu(MenuDTO dto); + + /** + * 根据ID查询菜单 + */ + MenuVO getMenuById(Long id); + + /** + * 查询菜单树 + */ + List getMenuTree(); + + /** + * 查询用户菜单树 + */ + List getUserMenuTree(Long userId); + + /** + * 删除菜单 + */ + boolean deleteMenu(Long id); +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/service/RoleService.java b/fund-sys/src/main/java/com/fundplatform/sys/service/RoleService.java new file mode 100644 index 0000000..1a2de7d --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/service/RoleService.java @@ -0,0 +1,53 @@ +package com.fundplatform.sys.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.sys.dto.RoleDTO; +import com.fundplatform.sys.vo.RoleVO; + +import java.util.List; + +/** + * 角色服务接口 + */ +public interface RoleService { + + /** + * 创建角色 + */ + Long createRole(RoleDTO dto); + + /** + * 更新角色 + */ + boolean updateRole(RoleDTO dto); + + /** + * 根据ID查询角色 + */ + RoleVO getRoleById(Long id); + + /** + * 分页查询角色 + */ + Page pageRoles(int pageNum, int pageSize, String roleName, Integer status); + + /** + * 查询所有角色 + */ + List listAllRoles(); + + /** + * 删除角色 + */ + boolean deleteRole(Long id); + + /** + * 更新角色状态 + */ + boolean updateStatus(Long id, Integer status); + + /** + * 分配菜单权限 + */ + boolean assignMenus(Long roleId, List menuIds); +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/service/UserService.java b/fund-sys/src/main/java/com/fundplatform/sys/service/UserService.java new file mode 100644 index 0000000..5bc5a1e --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/service/UserService.java @@ -0,0 +1,80 @@ +package com.fundplatform.sys.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.sys.dto.UserDTO; +import com.fundplatform.sys.vo.UserVO; + +/** + * 用户服务接口 + */ +public interface UserService { + + /** + * 创建用户 + * + * @param dto 用户DTO + * @return 用户ID + */ + Long createUser(UserDTO dto); + + /** + * 更新用户 + * + * @param dto 用户DTO + * @return 是否成功 + */ + boolean updateUser(UserDTO dto); + + /** + * 根据ID查询用户 + * + * @param id 用户ID + * @return 用户VO + */ + UserVO getUserById(Long id); + + /** + * 分页查询用户 + * + * @param pageNum 页码 + * @param pageSize 每页大小 + * @param username 用户名(模糊查询) + * @param status 状态 + * @param deptId 部门ID + * @return 分页数据 + */ + Page pageUsers(int pageNum, int pageSize, String username, Integer status, Long deptId); + + /** + * 删除用户 + * + * @param id 用户ID + * @return 是否成功 + */ + boolean deleteUser(Long id); + + /** + * 批量删除用户 + * + * @param ids 用户ID列表 + * @return 是否成功 + */ + boolean batchDeleteUsers(Long[] ids); + + /** + * 重置密码 + * + * @param id 用户ID + * @return 是否成功 + */ + boolean resetPassword(Long id); + + /** + * 更新用户状态 + * + * @param id 用户ID + * @param status 状态 + * @return 是否成功 + */ + boolean updateStatus(Long id, Integer status); +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/service/impl/DeptServiceImpl.java b/fund-sys/src/main/java/com/fundplatform/sys/service/impl/DeptServiceImpl.java new file mode 100644 index 0000000..df5a5fa --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/service/impl/DeptServiceImpl.java @@ -0,0 +1,180 @@ +package com.fundplatform.sys.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.fundplatform.sys.data.entity.SysDept; +import com.fundplatform.sys.data.service.SysDeptDataService; +import com.fundplatform.sys.dto.DeptDTO; +import com.fundplatform.sys.service.DeptService; +import com.fundplatform.sys.vo.DeptVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 部门服务实现 + */ +@Service +public class DeptServiceImpl implements DeptService { + + private static final Logger log = LoggerFactory.getLogger(DeptServiceImpl.class); + + private final SysDeptDataService deptDataService; + + public DeptServiceImpl(SysDeptDataService deptDataService) { + this.deptDataService = deptDataService; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createDept(DeptDTO dto) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysDept::getDeptCode, dto.getDeptCode()); + wrapper.eq(SysDept::getDeleted, 0); + if (deptDataService.count(wrapper) > 0) { + throw new RuntimeException("部门编码已存在"); + } + + SysDept dept = new SysDept(); + dept.setParentId(dto.getParentId() != null ? dto.getParentId() : 0L); + dept.setDeptCode(dto.getDeptCode()); + dept.setDeptName(dto.getDeptName()); + dept.setDeptLeader(dto.getDeptLeader()); + dept.setPhone(dto.getPhone()); + dept.setEmail(dto.getEmail()); + dept.setSortOrder(dto.getSortOrder() != null ? dto.getSortOrder() : 0); + dept.setStatus(dto.getStatus() != null ? dto.getStatus() : 1); + dept.setDeleted(0); + dept.setCreatedTime(LocalDateTime.now()); + + deptDataService.save(dept); + log.info("创建部门成功: deptId={}, deptName={}", dept.getId(), dept.getDeptName()); + return dept.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateDept(DeptDTO dto) { + if (dto.getId() == null) { + throw new RuntimeException("部门ID不能为空"); + } + + SysDept existing = deptDataService.getById(dto.getId()); + if (existing == null || existing.getDeleted() == 1) { + throw new RuntimeException("部门不存在"); + } + + SysDept dept = new SysDept(); + dept.setId(dto.getId()); + dept.setParentId(dto.getParentId()); + dept.setDeptName(dto.getDeptName()); + dept.setDeptLeader(dto.getDeptLeader()); + dept.setPhone(dto.getPhone()); + dept.setEmail(dto.getEmail()); + dept.setSortOrder(dto.getSortOrder()); + dept.setStatus(dto.getStatus()); + dept.setUpdatedTime(LocalDateTime.now()); + + boolean result = deptDataService.updateById(dept); + log.info("更新部门: deptId={}", dto.getId()); + return result; + } + + @Override + public DeptVO getDeptById(Long id) { + SysDept dept = deptDataService.getById(id); + if (dept == null || dept.getDeleted() == 1) { + throw new RuntimeException("部门不存在"); + } + return convertToVO(dept); + } + + @Override + public List getDeptTree() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysDept::getDeleted, 0); + wrapper.eq(SysDept::getStatus, 1); + wrapper.orderByAsc(SysDept::getSortOrder); + + List depts = deptDataService.list(wrapper); + return buildDeptTree(depts, 0L); + } + + @Override + public List listAllDepts() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysDept::getDeleted, 0); + wrapper.orderByAsc(SysDept::getSortOrder); + return deptDataService.list(wrapper).stream().map(this::convertToVO).toList(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteDept(Long id) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysDept::getParentId, id); + wrapper.eq(SysDept::getDeleted, 0); + if (deptDataService.count(wrapper) > 0) { + throw new RuntimeException("存在子部门,不能删除"); + } + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SysDept::getId, id); + updateWrapper.set(SysDept::getDeleted, 1); + updateWrapper.set(SysDept::getUpdatedTime, LocalDateTime.now()); + boolean result = deptDataService.update(updateWrapper); + log.info("删除部门: deptId={}", id); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateStatus(Long id, Integer status) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(SysDept::getId, id); + wrapper.set(SysDept::getStatus, status); + wrapper.set(SysDept::getUpdatedTime, LocalDateTime.now()); + boolean result = deptDataService.update(wrapper); + log.info("更新部门状态: deptId={}, status={}", id, status); + return result; + } + + private List buildDeptTree(List depts, Long parentId) { + List tree = new ArrayList<>(); + + Map> deptMap = depts.stream() + .collect(Collectors.groupingBy(SysDept::getParentId)); + + List rootDepts = deptMap.getOrDefault(parentId, new ArrayList<>()); + for (SysDept dept : rootDepts) { + DeptVO vo = convertToVO(dept); + vo.setChildren(buildDeptTree(depts, dept.getId())); + tree.add(vo); + } + return tree; + } + + private DeptVO convertToVO(SysDept dept) { + DeptVO vo = new DeptVO(); + vo.setId(dept.getId()); + vo.setParentId(dept.getParentId()); + vo.setDeptCode(dept.getDeptCode()); + vo.setDeptName(dept.getDeptName()); + vo.setDeptLeader(dept.getDeptLeader()); + vo.setPhone(dept.getPhone()); + vo.setEmail(dept.getEmail()); + vo.setSortOrder(dept.getSortOrder()); + vo.setStatus(dept.getStatus()); + vo.setTenantId(dept.getTenantId()); + vo.setCreatedTime(dept.getCreatedTime()); + vo.setUpdatedTime(dept.getUpdatedTime()); + return vo; + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/service/impl/MenuServiceImpl.java b/fund-sys/src/main/java/com/fundplatform/sys/service/impl/MenuServiceImpl.java new file mode 100644 index 0000000..72f05a2 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/service/impl/MenuServiceImpl.java @@ -0,0 +1,178 @@ +package com.fundplatform.sys.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.fundplatform.sys.data.entity.SysMenu; +import com.fundplatform.sys.data.service.SysMenuDataService; +import com.fundplatform.sys.dto.MenuDTO; +import com.fundplatform.sys.service.MenuService; +import com.fundplatform.sys.vo.MenuVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 菜单服务实现 + */ +@Service +public class MenuServiceImpl implements MenuService { + + private static final Logger log = LoggerFactory.getLogger(MenuServiceImpl.class); + + private final SysMenuDataService menuDataService; + + public MenuServiceImpl(SysMenuDataService menuDataService) { + this.menuDataService = menuDataService; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createMenu(MenuDTO dto) { + SysMenu menu = new SysMenu(); + menu.setParentId(dto.getParentId() != null ? dto.getParentId() : 0L); + menu.setMenuName(dto.getMenuName()); + menu.setMenuType(dto.getMenuType() != null ? dto.getMenuType() : 1); + menu.setMenuPath(dto.getMenuPath()); + menu.setMenuIcon(dto.getMenuIcon()); + menu.setComponent(dto.getComponent()); + menu.setPermission(dto.getPermission()); + menu.setSortOrder(dto.getSortOrder() != null ? dto.getSortOrder() : 0); + menu.setVisible(dto.getVisible() != null ? dto.getVisible() : 1); + menu.setStatus(dto.getStatus() != null ? dto.getStatus() : 1); + menu.setDeleted(0); + menu.setCreatedTime(LocalDateTime.now()); + + menuDataService.save(menu); + log.info("创建菜单成功: menuId={}, menuName={}", menu.getId(), menu.getMenuName()); + return menu.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateMenu(MenuDTO dto) { + if (dto.getId() == null) { + throw new RuntimeException("菜单ID不能为空"); + } + + SysMenu existing = menuDataService.getById(dto.getId()); + if (existing == null || existing.getDeleted() == 1) { + throw new RuntimeException("菜单不存在"); + } + + SysMenu menu = new SysMenu(); + menu.setId(dto.getId()); + menu.setParentId(dto.getParentId()); + menu.setMenuName(dto.getMenuName()); + menu.setMenuType(dto.getMenuType()); + menu.setMenuPath(dto.getMenuPath()); + menu.setMenuIcon(dto.getMenuIcon()); + menu.setComponent(dto.getComponent()); + menu.setPermission(dto.getPermission()); + menu.setSortOrder(dto.getSortOrder()); + menu.setVisible(dto.getVisible()); + menu.setStatus(dto.getStatus()); + menu.setUpdatedTime(LocalDateTime.now()); + + boolean result = menuDataService.updateById(menu); + log.info("更新菜单: menuId={}", dto.getId()); + return result; + } + + @Override + public MenuVO getMenuById(Long id) { + SysMenu menu = menuDataService.getById(id); + if (menu == null || menu.getDeleted() == 1) { + throw new RuntimeException("菜单不存在"); + } + return convertToVO(menu); + } + + @Override + public List getMenuTree() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysMenu::getDeleted, 0); + wrapper.eq(SysMenu::getStatus, 1); + wrapper.orderByAsc(SysMenu::getSortOrder); + + List menus = menuDataService.list(wrapper); + return buildMenuTree(menus, 0L); + } + + @Override + public List getUserMenuTree(Long userId) { + // TODO: 根据用户角色查询菜单 + return getMenuTree(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteMenu(Long id) { + // 检查是否有子菜单 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysMenu::getParentId, id); + wrapper.eq(SysMenu::getDeleted, 0); + if (menuDataService.count(wrapper) > 0) { + throw new RuntimeException("存在子菜单,不能删除"); + } + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SysMenu::getId, id); + updateWrapper.set(SysMenu::getDeleted, 1); + updateWrapper.set(SysMenu::getUpdatedTime, LocalDateTime.now()); + boolean result = menuDataService.update(updateWrapper); + log.info("删除菜单: menuId={}", id); + return result; + } + + private List buildMenuTree(List menus, Long parentId) { + List tree = new ArrayList<>(); + + Map> menuMap = menus.stream() + .collect(Collectors.groupingBy(SysMenu::getParentId)); + + List rootMenus = menuMap.getOrDefault(parentId, new ArrayList<>()); + for (SysMenu menu : rootMenus) { + MenuVO vo = convertToVO(menu); + vo.setChildren(buildMenuTree(menus, menu.getId())); + tree.add(vo); + } + return tree; + } + + private MenuVO convertToVO(SysMenu menu) { + MenuVO vo = new MenuVO(); + vo.setId(menu.getId()); + vo.setParentId(menu.getParentId()); + vo.setMenuName(menu.getMenuName()); + vo.setMenuType(menu.getMenuType()); + vo.setMenuTypeName(getMenuTypeName(menu.getMenuType())); + vo.setMenuPath(menu.getMenuPath()); + vo.setMenuIcon(menu.getMenuIcon()); + vo.setComponent(menu.getComponent()); + vo.setPermission(menu.getPermission()); + vo.setSortOrder(menu.getSortOrder()); + vo.setVisible(menu.getVisible()); + vo.setStatus(menu.getStatus()); + vo.setTenantId(menu.getTenantId()); + vo.setCreatedTime(menu.getCreatedTime()); + vo.setUpdatedTime(menu.getUpdatedTime()); + return vo; + } + + private String getMenuTypeName(Integer menuType) { + if (menuType == null) return ""; + switch (menuType) { + case 1: return "目录"; + case 2: return "菜单"; + case 3: return "按钮"; + default: return ""; + } + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/service/impl/RoleServiceImpl.java b/fund-sys/src/main/java/com/fundplatform/sys/service/impl/RoleServiceImpl.java new file mode 100644 index 0000000..e69ba69 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/service/impl/RoleServiceImpl.java @@ -0,0 +1,180 @@ +package com.fundplatform.sys.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.sys.data.entity.SysRole; +import com.fundplatform.sys.data.service.SysRoleDataService; +import com.fundplatform.sys.dto.RoleDTO; +import com.fundplatform.sys.service.RoleService; +import com.fundplatform.sys.vo.RoleVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 角色服务实现 + */ +@Service +public class RoleServiceImpl implements RoleService { + + private static final Logger log = LoggerFactory.getLogger(RoleServiceImpl.class); + + private final SysRoleDataService roleDataService; + + public RoleServiceImpl(SysRoleDataService roleDataService) { + this.roleDataService = roleDataService; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createRole(RoleDTO dto) { + // 检查角色编码是否存在 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysRole::getRoleCode, dto.getRoleCode()); + wrapper.eq(SysRole::getDeleted, 0); + if (roleDataService.count(wrapper) > 0) { + throw new RuntimeException("角色编码已存在"); + } + + SysRole role = new SysRole(); + role.setRoleCode(dto.getRoleCode()); + role.setRoleName(dto.getRoleName()); + role.setDataScope(dto.getDataScope() != null ? dto.getDataScope() : 1); + role.setStatus(dto.getStatus() != null ? dto.getStatus() : 1); + role.setSortOrder(dto.getSortOrder()); + role.setDeleted(0); + role.setCreatedTime(LocalDateTime.now()); + + roleDataService.save(role); + log.info("创建角色成功: roleId={}, roleCode={}", role.getId(), role.getRoleCode()); + return role.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateRole(RoleDTO dto) { + if (dto.getId() == null) { + throw new RuntimeException("角色ID不能为空"); + } + + SysRole existing = roleDataService.getById(dto.getId()); + if (existing == null || existing.getDeleted() == 1) { + throw new RuntimeException("角色不存在"); + } + + SysRole role = new SysRole(); + role.setId(dto.getId()); + role.setRoleName(dto.getRoleName()); + role.setDataScope(dto.getDataScope()); + role.setStatus(dto.getStatus()); + role.setSortOrder(dto.getSortOrder()); + role.setUpdatedTime(LocalDateTime.now()); + + boolean result = roleDataService.updateById(role); + log.info("更新角色: roleId={}, result={}", dto.getId(), result); + return result; + } + + @Override + public RoleVO getRoleById(Long id) { + SysRole role = roleDataService.getById(id); + if (role == null || role.getDeleted() == 1) { + throw new RuntimeException("角色不存在"); + } + return convertToVO(role); + } + + @Override + public Page pageRoles(int pageNum, int pageSize, String roleName, Integer status) { + Page page = new Page<>(pageNum, pageSize); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysRole::getDeleted, 0); + + if (StringUtils.hasText(roleName)) { + wrapper.like(SysRole::getRoleName, roleName); + } + if (status != null) { + wrapper.eq(SysRole::getStatus, status); + } + wrapper.orderByAsc(SysRole::getSortOrder); + + Page rolePage = roleDataService.page(page, wrapper); + + Page voPage = new Page<>(rolePage.getCurrent(), rolePage.getSize(), rolePage.getTotal()); + voPage.setRecords(rolePage.getRecords().stream().map(this::convertToVO).toList()); + return voPage; + } + + @Override + public List listAllRoles() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysRole::getDeleted, 0); + wrapper.eq(SysRole::getStatus, 1); + wrapper.orderByAsc(SysRole::getSortOrder); + return roleDataService.list(wrapper).stream().map(this::convertToVO).toList(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteRole(Long id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(SysRole::getId, id); + wrapper.set(SysRole::getDeleted, 1); + wrapper.set(SysRole::getUpdatedTime, LocalDateTime.now()); + boolean result = roleDataService.update(wrapper); + log.info("删除角色: roleId={}", id); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateStatus(Long id, Integer status) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(SysRole::getId, id); + wrapper.set(SysRole::getStatus, status); + wrapper.set(SysRole::getUpdatedTime, LocalDateTime.now()); + boolean result = roleDataService.update(wrapper); + log.info("更新角色状态: roleId={}, status={}", id, status); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean assignMenus(Long roleId, List menuIds) { + // TODO: 实现角色菜单关联 + log.info("分配角色菜单: roleId={}, menuIds={}", roleId, menuIds); + return true; + } + + private RoleVO convertToVO(SysRole role) { + RoleVO vo = new RoleVO(); + vo.setId(role.getId()); + vo.setRoleCode(role.getRoleCode()); + vo.setRoleName(role.getRoleName()); + vo.setDataScope(role.getDataScope()); + vo.setDataScopeName(getDataScopeName(role.getDataScope())); + vo.setStatus(role.getStatus()); + vo.setSortOrder(role.getSortOrder()); + vo.setTenantId(role.getTenantId()); + vo.setCreatedTime(role.getCreatedTime()); + vo.setUpdatedTime(role.getUpdatedTime()); + return vo; + } + + private String getDataScopeName(Integer dataScope) { + if (dataScope == null) return ""; + switch (dataScope) { + case 1: return "全部数据"; + case 2: return "本部门数据"; + case 3: return "本部门及下级数据"; + case 4: return "仅本人数据"; + default: return ""; + } + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/service/impl/UserServiceImpl.java b/fund-sys/src/main/java/com/fundplatform/sys/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..1f36aff --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/service/impl/UserServiceImpl.java @@ -0,0 +1,230 @@ +package com.fundplatform.sys.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fundplatform.sys.data.entity.SysDept; +import com.fundplatform.sys.data.entity.SysUser; +import com.fundplatform.sys.data.service.SysDeptDataService; +import com.fundplatform.sys.data.service.SysUserDataService; +import com.fundplatform.sys.dto.UserDTO; +import com.fundplatform.sys.service.UserService; +import com.fundplatform.sys.vo.UserVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.Arrays; + +/** + * 用户服务实现 + */ +@Service +public class UserServiceImpl implements UserService { + + private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class); + + private final SysUserDataService userDataService; + private final SysDeptDataService deptDataService; + private final BCryptPasswordEncoder passwordEncoder; + + public UserServiceImpl(SysUserDataService userDataService, SysDeptDataService deptDataService) { + this.userDataService = userDataService; + this.deptDataService = deptDataService; + this.passwordEncoder = new BCryptPasswordEncoder(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createUser(UserDTO dto) { + // 检查用户名是否存在 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysUser::getUsername, dto.getUsername()); + wrapper.eq(SysUser::getDeleted, 0); + if (userDataService.count(wrapper) > 0) { + throw new RuntimeException("用户名已存在"); + } + + // 创建用户 + SysUser user = new SysUser(); + user.setUsername(dto.getUsername()); + user.setPassword(passwordEncoder.encode(dto.getPassword())); + user.setRealName(dto.getRealName()); + user.setPhone(dto.getPhone()); + user.setEmail(dto.getEmail()); + user.setDeptId(dto.getDeptId()); + user.setStatus(dto.getStatus() != null ? dto.getStatus() : 1); + user.setAvatar(dto.getAvatar()); + user.setRemark(dto.getRemark()); + user.setDeleted(0); + user.setCreatedTime(LocalDateTime.now()); + + userDataService.save(user); + log.info("创建用户成功: userId={}, username={}", user.getId(), user.getUsername()); + return user.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateUser(UserDTO dto) { + if (dto.getId() == null) { + throw new RuntimeException("用户ID不能为空"); + } + + // 检查用户是否存在 + SysUser existingUser = userDataService.getById(dto.getId()); + if (existingUser == null || existingUser.getDeleted() == 1) { + throw new RuntimeException("用户不存在"); + } + + // 如果修改了用户名,检查是否重复 + if (StringUtils.hasText(dto.getUsername()) && !dto.getUsername().equals(existingUser.getUsername())) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysUser::getUsername, dto.getUsername()); + wrapper.eq(SysUser::getDeleted, 0); + wrapper.ne(SysUser::getId, dto.getId()); + if (userDataService.count(wrapper) > 0) { + throw new RuntimeException("用户名已存在"); + } + } + + // 更新用户 + SysUser user = new SysUser(); + user.setId(dto.getId()); + if (StringUtils.hasText(dto.getUsername())) { + user.setUsername(dto.getUsername()); + } + if (StringUtils.hasText(dto.getPassword())) { + user.setPassword(passwordEncoder.encode(dto.getPassword())); + } + user.setRealName(dto.getRealName()); + user.setPhone(dto.getPhone()); + user.setEmail(dto.getEmail()); + user.setDeptId(dto.getDeptId()); + user.setStatus(dto.getStatus()); + user.setAvatar(dto.getAvatar()); + user.setRemark(dto.getRemark()); + user.setUpdatedTime(LocalDateTime.now()); + + boolean result = userDataService.updateById(user); + log.info("更新用户: userId={}, result={}", dto.getId(), result); + return result; + } + + @Override + public UserVO getUserById(Long id) { + SysUser user = userDataService.getById(id); + if (user == null || user.getDeleted() == 1) { + throw new RuntimeException("用户不存在"); + } + return convertToVO(user); + } + + @Override + public Page pageUsers(int pageNum, int pageSize, String username, Integer status, Long deptId) { + Page page = new Page<>(pageNum, pageSize); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysUser::getDeleted, 0); + + if (StringUtils.hasText(username)) { + wrapper.like(SysUser::getUsername, username); + } + if (status != null) { + wrapper.eq(SysUser::getStatus, status); + } + if (deptId != null) { + wrapper.eq(SysUser::getDeptId, deptId); + } + wrapper.orderByDesc(SysUser::getCreatedTime); + + Page userPage = userDataService.page(page, wrapper); + + // 转换为VO + Page voPage = new Page<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal()); + voPage.setRecords(userPage.getRecords().stream().map(this::convertToVO).toList()); + return voPage; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteUser(Long id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(SysUser::getId, id); + wrapper.set(SysUser::getDeleted, 1); + wrapper.set(SysUser::getUpdatedTime, LocalDateTime.now()); + boolean result = userDataService.update(wrapper); + log.info("删除用户: userId={}, result={}", id, result); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean batchDeleteUsers(Long[] ids) { + if (ids == null || ids.length == 0) { + return false; + } + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.in(SysUser::getId, Arrays.asList(ids)); + wrapper.set(SysUser::getDeleted, 1); + wrapper.set(SysUser::getUpdatedTime, LocalDateTime.now()); + boolean result = userDataService.update(wrapper); + log.info("批量删除用户: ids={}, result={}", Arrays.toString(ids), result); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean resetPassword(Long id) { + String defaultPassword = "123456"; + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(SysUser::getId, id); + wrapper.set(SysUser::getPassword, passwordEncoder.encode(defaultPassword)); + wrapper.set(SysUser::getUpdatedTime, LocalDateTime.now()); + boolean result = userDataService.update(wrapper); + log.info("重置用户密码: userId={}", id); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateStatus(Long id, Integer status) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(SysUser::getId, id); + wrapper.set(SysUser::getStatus, status); + wrapper.set(SysUser::getUpdatedTime, LocalDateTime.now()); + boolean result = userDataService.update(wrapper); + log.info("更新用户状态: userId={}, status={}", id, status); + return result; + } + + /** + * 转换为VO + */ + private UserVO convertToVO(SysUser user) { + UserVO vo = new UserVO(); + vo.setId(user.getId()); + vo.setUsername(user.getUsername()); + vo.setRealName(user.getRealName()); + vo.setPhone(user.getPhone()); + vo.setEmail(user.getEmail()); + vo.setDeptId(user.getDeptId()); + vo.setStatus(user.getStatus()); + vo.setAvatar(user.getAvatar()); + vo.setTenantId(user.getTenantId()); + vo.setCreatedTime(user.getCreatedTime()); + vo.setUpdatedTime(user.getUpdatedTime()); + + // 查询部门名称 + if (user.getDeptId() != null) { + SysDept dept = deptDataService.getById(user.getDeptId()); + if (dept != null) { + vo.setDeptName(dept.getDeptName()); + } + } + return vo; + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/vo/DeptVO.java b/fund-sys/src/main/java/com/fundplatform/sys/vo/DeptVO.java new file mode 100644 index 0000000..d543fc4 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/vo/DeptVO.java @@ -0,0 +1,139 @@ +package com.fundplatform.sys.vo; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 部门VO + */ +public class DeptVO { + + private Long id; + private Long parentId; + private String parentName; + private String deptCode; + private String deptName; + private String deptLeader; + private String phone; + private String email; + private Integer sortOrder; + private Integer status; + private Long tenantId; + private LocalDateTime createdTime; + private LocalDateTime updatedTime; + + // 子部门 + private List children; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getParentName() { + return parentName; + } + + public void setParentName(String parentName) { + this.parentName = parentName; + } + + public String getDeptCode() { + return deptCode; + } + + public void setDeptCode(String deptCode) { + this.deptCode = deptCode; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptLeader() { + return deptLeader; + } + + public void setDeptLeader(String deptLeader) { + this.deptLeader = deptLeader; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Integer getSortOrder() { + return sortOrder; + } + + public void setSortOrder(Integer sortOrder) { + this.sortOrder = sortOrder; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + public LocalDateTime getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(LocalDateTime createdTime) { + this.createdTime = createdTime; + } + + public LocalDateTime getUpdatedTime() { + return updatedTime; + } + + public void setUpdatedTime(LocalDateTime updatedTime) { + this.updatedTime = updatedTime; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/vo/MenuVO.java b/fund-sys/src/main/java/com/fundplatform/sys/vo/MenuVO.java new file mode 100644 index 0000000..3bc5e44 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/vo/MenuVO.java @@ -0,0 +1,157 @@ +package com.fundplatform.sys.vo; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 菜单VO + */ +public class MenuVO { + + private Long id; + private Long parentId; + private String menuName; + private Integer menuType; + private String menuTypeName; + private String menuPath; + private String menuIcon; + private String component; + private String permission; + private Integer sortOrder; + private Integer visible; + private Integer status; + private Long tenantId; + private LocalDateTime createdTime; + private LocalDateTime updatedTime; + + // 子菜单 + private List children; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getMenuName() { + return menuName; + } + + public void setMenuName(String menuName) { + this.menuName = menuName; + } + + public Integer getMenuType() { + return menuType; + } + + public void setMenuType(Integer menuType) { + this.menuType = menuType; + } + + public String getMenuTypeName() { + return menuTypeName; + } + + public void setMenuTypeName(String menuTypeName) { + this.menuTypeName = menuTypeName; + } + + public String getMenuPath() { + return menuPath; + } + + public void setMenuPath(String menuPath) { + this.menuPath = menuPath; + } + + public String getMenuIcon() { + return menuIcon; + } + + public void setMenuIcon(String menuIcon) { + this.menuIcon = menuIcon; + } + + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public Integer getSortOrder() { + return sortOrder; + } + + public void setSortOrder(Integer sortOrder) { + this.sortOrder = sortOrder; + } + + public Integer getVisible() { + return visible; + } + + public void setVisible(Integer visible) { + this.visible = visible; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + public LocalDateTime getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(LocalDateTime createdTime) { + this.createdTime = createdTime; + } + + public LocalDateTime getUpdatedTime() { + return updatedTime; + } + + public void setUpdatedTime(LocalDateTime updatedTime) { + this.updatedTime = updatedTime; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/vo/RoleVO.java b/fund-sys/src/main/java/com/fundplatform/sys/vo/RoleVO.java new file mode 100644 index 0000000..89e3816 --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/vo/RoleVO.java @@ -0,0 +1,100 @@ +package com.fundplatform.sys.vo; + +import java.time.LocalDateTime; + +/** + * 角色VO + */ +public class RoleVO { + + private Long id; + private String roleCode; + private String roleName; + private Integer dataScope; + private String dataScopeName; + private Integer status; + private Integer sortOrder; + private Long tenantId; + private LocalDateTime createdTime; + private LocalDateTime updatedTime; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getRoleCode() { + return roleCode; + } + + public void setRoleCode(String roleCode) { + this.roleCode = roleCode; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public Integer getDataScope() { + return dataScope; + } + + public void setDataScope(Integer dataScope) { + this.dataScope = dataScope; + } + + public String getDataScopeName() { + return dataScopeName; + } + + public void setDataScopeName(String dataScopeName) { + this.dataScopeName = dataScopeName; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getSortOrder() { + return sortOrder; + } + + public void setSortOrder(Integer sortOrder) { + this.sortOrder = sortOrder; + } + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + public LocalDateTime getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(LocalDateTime createdTime) { + this.createdTime = createdTime; + } + + public LocalDateTime getUpdatedTime() { + return updatedTime; + } + + public void setUpdatedTime(LocalDateTime updatedTime) { + this.updatedTime = updatedTime; + } +} diff --git a/fund-sys/src/main/java/com/fundplatform/sys/vo/UserVO.java b/fund-sys/src/main/java/com/fundplatform/sys/vo/UserVO.java new file mode 100644 index 0000000..4e70bec --- /dev/null +++ b/fund-sys/src/main/java/com/fundplatform/sys/vo/UserVO.java @@ -0,0 +1,118 @@ +package com.fundplatform.sys.vo; + +import java.time.LocalDateTime; + +/** + * 用户VO(返回视图) + */ +public class UserVO { + + private Long id; + private String username; + private String realName; + private String phone; + private String email; + private Long deptId; + private String deptName; + private Integer status; + private String avatar; + private Long tenantId; + private LocalDateTime createdTime; + private LocalDateTime updatedTime; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getRealName() { + return realName; + } + + public void setRealName(String realName) { + this.realName = realName; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Long getDeptId() { + return deptId; + } + + public void setDeptId(Long deptId) { + this.deptId = deptId; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + public LocalDateTime getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(LocalDateTime createdTime) { + this.createdTime = createdTime; + } + + public LocalDateTime getUpdatedTime() { + return updatedTime; + } + + public void setUpdatedTime(LocalDateTime updatedTime) { + this.updatedTime = updatedTime; + } +} diff --git a/fund-sys/src/main/resources/application.yml b/fund-sys/src/main/resources/application.yml index 4e8ac36..2ce6595 100644 --- a/fund-sys/src/main/resources/application.yml +++ b/fund-sys/src/main/resources/application.yml @@ -5,6 +5,13 @@ spring: application: name: fund-sys + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: fund-platform + group: DEFAULT_GROUP + datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/fund_sys?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai @@ -15,6 +22,21 @@ spring: minimum-idle: 5 connection-timeout: 30000 + # Redis配置 + data: + redis: + host: localhost + port: 6379 + password: zjf@123456 + database: 0 + timeout: 10000 + lettuce: + pool: + max-active: 8 + max-wait: -1 + max-idle: 8 + min-idle: 0 + mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml type-aliases-package: com.fundplatform.sys.data.entity diff --git a/fund-sys/src/main/resources/logback-spring.xml b/fund-sys/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..cb1b377 --- /dev/null +++ b/fund-sys/src/main/resources/logback-spring.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}] %-5level %logger{50} - %msg%n + UTF-8 + + + + + + ${LOG_PATH}/${APP_NAME}/info.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}] %-5level %logger{50} - %msg%n + UTF-8 + + + ${LOG_PATH}/${APP_NAME}/info-%d{yyyy-MM-dd}.%i.log + + 100MB + + 30 + + + INFO + ACCEPT + DENY + + + + + + ${LOG_PATH}/${APP_NAME}/error.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}] %-5level %logger{50} - %msg%n + UTF-8 + + + ${LOG_PATH}/${APP_NAME}/error-%d{yyyy-MM-dd}.%i.log + + 100MB + + 30 + + + ERROR + ACCEPT + DENY + + + + + + ${LOG_PATH}/${APP_NAME}/json.log + + {"app_name":"${APP_NAME}"} + traceId + userId + tenantId + + + ${LOG_PATH}/${APP_NAME}/json-%d{yyyy-MM-dd}.%i.log + + 100MB + + 30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 3303762..e80688c 100644 --- a/pom.xml +++ b/pom.xml @@ -35,8 +35,31 @@ 21 + 2023.0.0 + 2023.0.0.0-RC1 + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba.version} + pom + import + + + +