feat: 新增本地开发启动脚本及各模块代码优化

- 新增 scripts/dev-start.sh: 本地开发环境启动脚本,支持启动所有/核心/单个服务
- 新增 scripts/dev-stop.sh: 本地开发环境停止脚本
- 新增 scripts/dev-status.sh: 服务状态检查脚本
- 优化各模块 Controller/Service/VO/DTO 代码
- 删除过时的计划文件
This commit is contained in:
zhangjf 2026-03-02 22:46:31 +08:00
parent 9a55286869
commit cc6577b3df
30 changed files with 914 additions and 517 deletions

View File

@ -1,298 +0,0 @@
# 资金平台功能完善与架构增强计划
## 前置条件
- ✅ 六步实施已完成
- ✅ 11个模块编译通过
- ✅ 数据库初始化完成
- ✅ fund-sys服务运行正常
- ✅ Gateway路由配置完成
---
## A. 完善fund-sys权限管理
### A.1 用户管理模块
**目标**: 实现用户CRUD接口
**实施步骤**:
1. 创建 `UserDTO.java` (用户创建/更新DTO)
2. 创建 `UserVO.java` (用户返回VO)
3. 创建 `UserService.java``UserServiceImpl.java`
4. 创建 `UserController.java` (REST接口)
5. 实现接口:
- `POST /api/v1/sys/user` - 创建用户
- `PUT /api/v1/sys/user/{id}` - 更新用户
- `GET /api/v1/sys/user/{id}` - 查询用户
- `GET /api/v1/sys/user/page` - 分页查询
- `DELETE /api/v1/sys/user/{id}` - 删除用户
**涉及文件**:
- `fund-sys/src/main/java/com/fundplatform/sys/dto/UserDTO.java`
- `fund-sys/src/main/java/com/fundplatform/sys/vo/UserVO.java`
- `fund-sys/src/main/java/com/fundplatform/sys/service/UserService.java`
- `fund-sys/src/main/java/com/fundplatform/sys/service/impl/UserServiceImpl.java`
- `fund-sys/src/main/java/com/fundplatform/sys/controller/UserController.java`
### A.2 角色管理模块
**实施步骤**: 同用户管理,创建Role相关类
**接口**: `/api/v1/sys/role` 系列接口
### A.3 菜单管理模块
**实施步骤**: 同用户管理,创建Menu相关类
**接口**: `/api/v1/sys/menu` 系列接口
### A.4 部门管理模块
**实施步骤**: 同用户管理,创建Dept相关类
**接口**: `/api/v1/sys/dept` 系列接口
**验证**: 每个模块完成后执行 `mvn -pl fund-sys clean install`
---
## B. 完善其他业务模块
### B.1 fund-req用款申请模块
**目标**: 实现用款申请完整业务流程
**实施步骤**:
1. 创建 `FundRequest.java` 实体类
2. 创建 `FundRequestMapper.java``FundRequestDataService.java`
3. 创建 `FundRequestService.java` 和实现类
4. 创建 `FundRequestController.java`
5. 实现接口:
- `POST /api/v1/req/fund-request` - 创建申请
- `PUT /api/v1/req/fund-request/{id}/approve` - 审批通过
- `PUT /api/v1/req/fund-request/{id}/reject` - 审批拒绝
- `GET /api/v1/req/fund-request/page` - 分页查询
**涉及文件**:
- `fund-req/src/main/java/com/fundplatform/req/data/entity/FundRequest.java`
- `fund-req/src/main/java/com/fundplatform/req/data/mapper/FundRequestMapper.java`
- `fund-req/src/main/java/com/fundplatform/req/data/service/FundRequestDataService.java`
- `fund-req/src/main/java/com/fundplatform/req/service/FundRequestService.java`
- `fund-req/src/main/java/com/fundplatform/req/controller/FundRequestController.java`
### B.2 fund-exp支出管理模块
**实施步骤**: 类似fund-req,创建支出相关类
### B.3 fund-receipt收款管理模块
**实施步骤**: 类似fund-req,创建收款相关类
---
## C. Gateway增强
### C.1 全局日志过滤器
**目标**: 记录所有请求日志,生成TraceId
**实施步骤**:
1. 创建 `GlobalLogFilter.java` (实现GatewayFilter)
2. 生成TraceId并写入请求头
3. 记录请求路径、方法、耗时
4. 配置到application.yml
**涉及文件**:
- `fund-gateway/src/main/java/com/fundplatform/gateway/filter/GlobalLogFilter.java`
- `fund-gateway/src/main/resources/application.yml`
### C.2 JWT鉴权过滤器
**目标**: 验证JWT Token,提取用户信息
**实施步骤**:
1. 创建 `JwtAuthFilter.java`
2. 验证Token有效性
3. 提取userId、tenantId写入请求头
4. 配置白名单路径(登录、健康检查等)
**涉及文件**:
- `fund-gateway/src/main/java/com/fundplatform/gateway/filter/JwtAuthFilter.java`
- `fund-gateway/src/main/resources/application.yml`
### C.3 限流配置
**实施步骤**:
1. 添加Redis依赖
2. 配置RequestRateLimiter过滤器
3. 设置限流规则(每秒请求数)
---
## D. 服务治理
### D.1 集成Nacos服务注册发现
**目标**: 实现服务自动注册和发现
**实施步骤**:
1. 添加Nacos依赖到所有服务
2. 配置Nacos地址(从memory获取: localhost:8848)
3. 配置服务名称和分组
4. 启动服务验证注册成功
**涉及文件**:
- 所有服务的 `pom.xml` 添加nacos依赖
- 所有服务的 `application.yml` 添加nacos配置
### D.2 集成Sentinel熔断降级
**实施步骤**:
1. 添加Sentinel依赖
2. 配置熔断规则
3. 配置降级逻辑
### D.3 统一配置中心
**实施步骤**:
1. 在Nacos创建配置文件
2. 各服务从Nacos读取配置
---
## E. 日志与监控
### E.1 集成ELK日志聚合
**目标**: 统一日志收集和分析
**实施步骤**:
1. 配置Logback输出JSON格式日志
2. 部署Filebeat收集日志
3. 部署Logstash处理日志
4. 部署Elasticsearch存储日志
5. 部署Kibana可视化
### E.2 集成SkyWalking链路追踪
**实施步骤**:
1. 添加SkyWalking Agent
2. 配置链路追踪
3. 集成Gateway和Feign
### E.3 Prometheus+Grafana监控
**实施步骤**:
1. 添加Micrometer依赖
2. 配置Prometheus端点
3. 部署Prometheus采集指标
4. 部署Grafana可视化
---
## F. 数据库优化
### F.1 读写分离配置
**实施步骤**:
1. 配置主从数据库
2. 使用ShardingSphere实现读写分离
3. 配置路由规则
### F.2 连接池优化
**实施步骤**:
1. 优化HikariCP配置(连接数、超时时间)
2. 配置连接池监控
### F.3 索引优化
**实施步骤**:
1. 分析慢查询SQL
2. 添加必要索引
3. 优化查询语句
---
## G. 高可用架构
### G.1 服务集群部署
**实施步骤**:
1. 各服务打包为Docker镜像
2. 配置Docker Compose或K8s部署
3. 配置负载均衡
### G.2 网关集群部署
**实施步骤**:
1. Gateway多实例部署
2. 配置Nginx负载均衡
### G.3 数据库高可用
**实施步骤**:
1. 配置MySQL主从复制
2. 配置自动故障切换
---
## H. 安全增强
### H.1 HTTPS配置
**实施步骤**:
1. 生成SSL证书
2. 配置Spring Boot HTTPS
3. 配置Gateway HTTPS
### H.2 API签名验证
**实施步骤**:
1. 设计签名算法(时间戳+密钥+签名)
2. 创建签名拦截器
3. 各服务集成签名验证
### H.3 数据加密存储
**实施步骤**:
1. 敏感字段加密(手机号、身份证等)
2. 使用AES或RSA加密
3. 配置加解密工具类
---
## I. 性能优化
### I.1 Redis缓存集成
**目标**: 减少数据库查询压力
**实施步骤**:
1. 添加Redis依赖(从memory获取密码: zjf@123456)
2. 配置Redis连接
3. 实现缓存工具类
4. 对热点数据添加缓存
**涉及文件**:
- `fund-common/src/main/java/com/fundplatform/common/cache/RedisService.java`
- 所有服务的 `pom.xml` 添加redis依赖
- 所有服务的 `application.yml` 添加redis配置
### I.2 接口性能测试
**实施步骤**:
1. 使用JMeter编写测试脚本
2. 压测核心接口
3. 分析性能瓶颈
4. 优化慢接口
### I.3 异步处理优化
**实施步骤**:
1. 引入消息队列(RabbitMQ/Kafka)
2. 异步处理耗时操作
3. 实现最终一致性
---
## 实施优先级
| 优先级 | 模块 | 预计时间 | 依赖 |
|--------|------|----------|------|
| P0 | C.1 Gateway日志过滤器 | 2小时 | 无 |
| P0 | C.2 Gateway鉴权过滤器 | 3小时 | 无 |
| P1 | A.1 用户管理模块 | 4小时 | 无 |
| P1 | B.1 fund-req用款申请 | 4小时 | 无 |
| P1 | D.1 Nacos服务注册 | 4小时 | 无 |
| P2 | I.1 Redis缓存集成 | 4小时 | 无 |
| P2 | E.1 ELK日志聚合 | 1天 | 无 |
| P3 | 其他模块 | 按需 | 前置模块 |
---
## 验证标准
每个阶段完成后需验证:
1. ✅ 编译通过: `mvn clean install`
2. ✅ 服务启动正常
3. ✅ 接口测试通过
4. ✅ Git提交代码
---
## 风险提示
1. **Spring Cloud版本兼容性**: 注意Spring Boot 3.2与Spring Cloud版本匹配
2. **数据库性能**: 注意索引优化,避免慢查询
3. **服务稳定性**: 添加熔断降级,防止雪崩
4. **安全性**: API接口必须鉴权,敏感数据加密

View File

@ -28,7 +28,7 @@ public class ExpenseTypeController {
* 创建支出类型
*/
@PostMapping
public Result<Long> create(@Valid @RequestBody ExpenseTypeDTO dto) {
public Result<String> create(@Valid @RequestBody ExpenseTypeDTO dto) {
return Result.success(typeService.createType(dto));
}
@ -36,7 +36,7 @@ public class ExpenseTypeController {
* 更新支出类型
*/
@PutMapping("/{id}")
public Result<Boolean> update(@PathVariable Long id, @Valid @RequestBody ExpenseTypeDTO dto) {
public Result<Boolean> update(@PathVariable String id, @Valid @RequestBody ExpenseTypeDTO dto) {
dto.setId(id);
return Result.success(typeService.updateType(dto));
}
@ -72,7 +72,7 @@ public class ExpenseTypeController {
* 根据ID查询支出类型
*/
@GetMapping("/{id}")
public Result<ExpenseTypeVO> getById(@PathVariable Long id) {
public Result<ExpenseTypeVO> getById(@PathVariable String id) {
return Result.success(typeService.getTypeById(id));
}
@ -80,7 +80,7 @@ public class ExpenseTypeController {
* 查询指定父级的子类型列表
*/
@GetMapping("/children")
public Result<List<ExpenseTypeVO>> getChildren(@RequestParam(required = false) Long parentId) {
public Result<List<ExpenseTypeVO>> getChildren(@RequestParam(required = false) String parentId) {
return Result.success(typeService.getChildrenByParentId(parentId));
}
@ -88,7 +88,7 @@ public class ExpenseTypeController {
* 删除支出类型
*/
@DeleteMapping("/{id}")
public Result<Boolean> delete(@PathVariable Long id) {
public Result<Boolean> delete(@PathVariable String id) {
return Result.success(typeService.deleteType(id));
}
@ -96,7 +96,7 @@ public class ExpenseTypeController {
* 更新状态
*/
@PutMapping("/{id}/status")
public Result<Boolean> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
public Result<Boolean> updateStatus(@PathVariable String id, @RequestParam Integer status) {
return Result.success(typeService.updateStatus(id, status));
}
}

View File

@ -38,7 +38,7 @@ public class FundExpenseController {
* 创建支出申请
*/
@PostMapping
public Result<Long> create(@Valid @RequestBody FundExpenseDTO dto) {
public Result<String> create(@Valid @RequestBody FundExpenseDTO dto) {
return Result.success(expenseService.createExpense(dto));
}
@ -46,7 +46,7 @@ public class FundExpenseController {
* 更新支出申请仅待审批状态可修改
*/
@PutMapping("/{id}")
public Result<Boolean> update(@PathVariable Long id, @Valid @RequestBody FundExpenseDTO dto) {
public Result<Boolean> update(@PathVariable String id, @Valid @RequestBody FundExpenseDTO dto) {
dto.setId(id);
return Result.success(expenseService.updateExpense(dto));
}
@ -55,7 +55,7 @@ public class FundExpenseController {
* 根据ID查询支出详情
*/
@GetMapping("/{id}")
public Result<FundExpenseVO> getById(@PathVariable Long id) {
public Result<FundExpenseVO> getById(@PathVariable String id) {
return Result.success(expenseService.getExpenseById(id));
}
@ -67,7 +67,7 @@ public class FundExpenseController {
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize,
@RequestParam(required = false) String title,
@RequestParam(required = false) Long expenseType,
@RequestParam(required = false) String expenseType,
@RequestParam(required = false) Integer payStatus,
@RequestParam(required = false) Integer approvalStatus) {
Page<FundExpenseVO> page = expenseService.pageExpenses(pageNum, pageSize, title, expenseType, payStatus, approvalStatus);
@ -84,7 +84,7 @@ public class FundExpenseController {
* 删除支出申请仅待审批状态可删除
*/
@DeleteMapping("/{id}")
public Result<Boolean> delete(@PathVariable Long id) {
public Result<Boolean> delete(@PathVariable String id) {
return Result.success(expenseService.deleteExpense(id));
}
@ -92,7 +92,7 @@ public class FundExpenseController {
* 提交审批待审批 -> 审批中
*/
@PostMapping("/{id}/submit")
public Result<Boolean> submitApproval(@PathVariable Long id) {
public Result<Boolean> submitApproval(@PathVariable String id) {
return Result.success(expenseService.submitApproval(id));
}
@ -100,7 +100,7 @@ public class FundExpenseController {
* 撤回审批审批中 -> 待审批
*/
@PostMapping("/{id}/withdraw")
public Result<Boolean> withdrawApproval(@PathVariable Long id) {
public Result<Boolean> withdrawApproval(@PathVariable String id) {
return Result.success(expenseService.withdrawApproval(id));
}
@ -109,9 +109,9 @@ public class FundExpenseController {
*/
@PutMapping("/{id}/approve")
public Result<Boolean> approve(
@PathVariable Long id,
@PathVariable String id,
@RequestParam(required = false) String comment,
@RequestHeader(value = "X-User-Id", required = false) Long approverId) {
@RequestHeader(value = "X-User-Id", required = false) String approverId) {
return Result.success(expenseService.approve(id, approverId, comment));
}
@ -120,9 +120,9 @@ public class FundExpenseController {
*/
@PutMapping("/{id}/reject")
public Result<Boolean> reject(
@PathVariable Long id,
@PathVariable String id,
@RequestParam(required = false) String comment,
@RequestHeader(value = "X-User-Id", required = false) Long approverId) {
@RequestHeader(value = "X-User-Id", required = false) String approverId) {
return Result.success(expenseService.reject(id, approverId, comment));
}
@ -131,7 +131,7 @@ public class FundExpenseController {
*/
@PutMapping("/{id}/confirm-pay")
public Result<Boolean> confirmPay(
@PathVariable Long id,
@PathVariable String id,
@RequestParam String payChannel,
@RequestParam(required = false) String payVoucher) {
return Result.success(expenseService.confirmPay(id, payChannel, payVoucher));
@ -141,7 +141,7 @@ public class FundExpenseController {
* 标记支付失败
*/
@PutMapping("/{id}/pay-failed")
public Result<Boolean> payFailed(@PathVariable Long id, @RequestParam String reason) {
public Result<Boolean> payFailed(@PathVariable String id, @RequestParam String reason) {
return Result.success(expenseService.payFailed(id, reason));
}
@ -185,7 +185,7 @@ public class FundExpenseController {
@GetMapping("/export")
public void exportExcel(
@RequestParam(required = false) String title,
@RequestParam(required = false) Long expenseType,
@RequestParam(required = false) String expenseType,
@RequestParam(required = false) Integer payStatus,
@RequestParam(required = false) Integer approvalStatus,
HttpServletResponse response) {

View File

@ -9,7 +9,7 @@ import java.time.LocalDateTime;
public class FundExpenseDTO {
private Long id;
private String id;
@NotBlank(message = "支出标题不能为空")
private String title;
@ -20,8 +20,8 @@ public class FundExpenseDTO {
private String currency = "CNY";
@NotNull(message = "支出类型不能为空")
private Long expenseType;
@NotBlank(message = "支出类型不能为空")
private String expenseType;
@NotBlank(message = "收款单位不能为空")
private String payeeName;
@ -30,23 +30,23 @@ public class FundExpenseDTO {
private String payeeAccount;
private LocalDateTime expenseDate;
private String purpose;
private Long requestId;
private Long projectId;
private Long customerId;
private String requestId;
private String projectId;
private String 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 getId() { return id; }
public void setId(String 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 Long getExpenseType() { return expenseType; }
public void setExpenseType(Long expenseType) { this.expenseType = expenseType; }
public String getExpenseType() { return expenseType; }
public void setExpenseType(String expenseType) { this.expenseType = expenseType; }
public String getPayeeName() { return payeeName; }
public void setPayeeName(String payeeName) { this.payeeName = payeeName; }
public String getPayeeBank() { return payeeBank; }
@ -57,12 +57,12 @@ public class FundExpenseDTO {
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 getRequestId() { return requestId; }
public void setRequestId(String requestId) { this.requestId = requestId; }
public String getProjectId() { return projectId; }
public void setProjectId(String projectId) { this.projectId = projectId; }
public String getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; }
public String getAttachments() { return attachments; }
public void setAttachments(String attachments) { this.attachments = attachments; }
public String getRemark() { return remark; }

View File

@ -6,50 +6,50 @@ import com.fundplatform.exp.vo.FundExpenseVO;
public interface FundExpenseService {
Long createExpense(FundExpenseDTO dto);
String createExpense(FundExpenseDTO dto);
boolean updateExpense(FundExpenseDTO dto);
FundExpenseVO getExpenseById(Long id);
FundExpenseVO getExpenseById(String id);
Page<FundExpenseVO> pageExpenses(int pageNum, int pageSize, String title, Long expenseType, Integer payStatus, Integer approvalStatus);
Page<FundExpenseVO> pageExpenses(int pageNum, int pageSize, String title, String expenseType, Integer payStatus, Integer approvalStatus);
/**
* 查询支出列表不分页用于导出
*/
java.util.List<FundExpenseVO> listExpenses(String title, Long expenseType, Integer payStatus, Integer approvalStatus);
java.util.List<FundExpenseVO> listExpenses(String title, String expenseType, Integer payStatus, Integer approvalStatus);
boolean deleteExpense(Long id);
boolean deleteExpense(String id);
/**
* 提交审批待审批 -> 审批中
*/
boolean submitApproval(Long id);
boolean submitApproval(String id);
/**
* 审批通过审批中 -> 审批通过
*/
boolean approve(Long id, Long approverId, String comment);
boolean approve(String id, String approverId, String comment);
/**
* 审批拒绝审批中 -> 审批拒绝
*/
boolean reject(Long id, Long approverId, String comment);
boolean reject(String id, String approverId, String comment);
/**
* 撤回审批审批中 -> 待审批
*/
boolean withdrawApproval(Long id);
boolean withdrawApproval(String id);
/**
* 确认支付审批通过后才能支付
*/
boolean confirmPay(Long id, String payChannel, String payVoucher);
boolean confirmPay(String id, String payChannel, String payVoucher);
/**
* 支付失败
*/
boolean payFailed(Long id, String reason);
boolean payFailed(String id, String reason);
// ===================== 统计相关 =====================

View File

@ -56,7 +56,7 @@ public class FundExpenseServiceImpl implements FundExpenseService {
@Override
@Transactional(rollbackFor = Exception.class)
public Long createExpense(FundExpenseDTO dto) {
public String createExpense(FundExpenseDTO dto) {
FundExpense expense = new FundExpense();
expense.setExpenseNo(generateExpenseNo());
expense.setTitle(dto.getTitle());
@ -78,10 +78,10 @@ public class FundExpenseServiceImpl implements FundExpenseService {
expense.setCreatedTime(LocalDateTime.now());
// 获取租户ID和用户ID如果没有则使用默认值
Long tenantId = TenantContextHolder.getTenantId();
expense.setTenantId(tenantId != null ? tenantId : 1L);
Long userId = UserContextHolder.getUserId();
expense.setCreatedBy(userId != null ? userId : 1L);
String tenantId = TenantContextHolder.getTenantId();
expense.setTenantId(tenantId != null ? tenantId : "1");
String userId = UserContextHolder.getUserId();
expense.setCreatedBy(userId != null ? userId : "1");
expenseDataService.save(expense);
log.info("创建支出记录成功: id={}, expenseNo={}", expense.getId(), expense.getExpenseNo());
@ -112,14 +112,14 @@ public class FundExpenseServiceImpl implements FundExpenseService {
}
@Override
public FundExpenseVO getExpenseById(Long id) {
public FundExpenseVO getExpenseById(String id) {
FundExpense expense = expenseDataService.getById(id);
if (expense == null || expense.getDeleted() == 1) throw new RuntimeException("支出记录不存在");
return convertToVO(expense);
}
@Override
public Page<FundExpenseVO> pageExpenses(int pageNum, int pageSize, String title, Long expenseType, Integer payStatus, Integer approvalStatus) {
public Page<FundExpenseVO> pageExpenses(int pageNum, int pageSize, String title, String expenseType, Integer payStatus, Integer approvalStatus) {
Page<FundExpense> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<FundExpense> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(FundExpense::getDeleted, 0);
@ -136,7 +136,7 @@ public class FundExpenseServiceImpl implements FundExpenseService {
}
@Override
public List<FundExpenseVO> listExpenses(String title, Long expenseType, Integer payStatus, Integer approvalStatus) {
public List<FundExpenseVO> listExpenses(String title, String expenseType, Integer payStatus, Integer approvalStatus) {
LambdaQueryWrapper<FundExpense> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(FundExpense::getDeleted, 0);
if (StringUtils.hasText(title)) wrapper.like(FundExpense::getTitle, title);
@ -150,7 +150,7 @@ public class FundExpenseServiceImpl implements FundExpenseService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteExpense(Long id) {
public boolean deleteExpense(String id) {
FundExpense existing = expenseDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("支出记录不存在");
@ -170,7 +170,7 @@ public class FundExpenseServiceImpl implements FundExpenseService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean submitApproval(Long id) {
public boolean submitApproval(String id) {
FundExpense existing = expenseDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("支出记录不存在");
@ -195,7 +195,7 @@ public class FundExpenseServiceImpl implements FundExpenseService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean approve(Long id, Long approverId, String comment) {
public boolean approve(String id, String approverId, String comment) {
FundExpense existing = expenseDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("支出记录不存在");
@ -223,7 +223,7 @@ public class FundExpenseServiceImpl implements FundExpenseService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean reject(Long id, Long approverId, String comment) {
public boolean reject(String id, String approverId, String comment) {
FundExpense existing = expenseDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("支出记录不存在");
@ -251,7 +251,7 @@ public class FundExpenseServiceImpl implements FundExpenseService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean withdrawApproval(Long id) {
public boolean withdrawApproval(String id) {
FundExpense existing = expenseDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("支出记录不存在");
@ -276,7 +276,7 @@ public class FundExpenseServiceImpl implements FundExpenseService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean confirmPay(Long id, String payChannel, String payVoucher) {
public boolean confirmPay(String id, String payChannel, String payVoucher) {
FundExpense existing = expenseDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("支出记录不存在");
@ -307,7 +307,7 @@ public class FundExpenseServiceImpl implements FundExpenseService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean payFailed(Long id, String reason) {
public boolean payFailed(String id, String reason) {
FundExpense existing = expenseDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("支出记录不存在");
@ -371,9 +371,14 @@ public class FundExpenseServiceImpl implements FundExpenseService {
return vo;
}
private String getExpenseTypeName(Long type) {
if (type == null) return "";
return switch (type.intValue()) { case 1 -> "日常支出"; case 2 -> "项目支出"; case 3 -> "工资发放"; case 4 -> "其他"; default -> ""; };
private String getExpenseTypeName(String type) {
if (type == null || type.isEmpty()) return "";
try {
int typeValue = Integer.parseInt(type);
return switch (typeValue) { case 1 -> "日常支出"; case 2 -> "项目支出"; case 3 -> "工资发放"; case 4 -> "其他"; default -> ""; };
} catch (NumberFormatException e) {
return "";
}
}
private String getPayStatusName(Integer status) {
@ -447,19 +452,19 @@ public class FundExpenseServiceImpl implements FundExpenseService {
List<FundExpense> expenses = expenseDataService.list(wrapper);
Map<Long, BigDecimal> typeAmountMap = new HashMap<>();
Map<Long, Integer> typeCountMap = new HashMap<>();
Map<String, BigDecimal> typeAmountMap = new HashMap<>();
Map<String, Integer> typeCountMap = new HashMap<>();
for (FundExpense expense : expenses) {
Long type = expense.getExpenseType();
if (type == null) type = 0L;
String type = expense.getExpenseType();
if (type == null) type = "0";
typeAmountMap.merge(type, expense.getAmount() != null ? expense.getAmount() : BigDecimal.ZERO, BigDecimal::add);
typeCountMap.merge(type, 1, Integer::sum);
}
List<Map<String, Object>> result = new ArrayList<>();
for (Long type : typeAmountMap.keySet()) {
for (String type : typeAmountMap.keySet()) {
Map<String, Object> item = new HashMap<>();
item.put("type", type);
item.put("name", getExpenseTypeName(type));

View File

@ -8,26 +8,21 @@ import java.time.LocalDateTime;
public class FundExpenseVO {
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
private String id;
private String expenseNo;
private String title;
private BigDecimal amount;
private String currency;
@JsonSerialize(using = ToStringSerializer.class)
private Long expenseType;
private String expenseType;
private String expenseTypeName;
private String payeeName;
private String payeeBank;
private String payeeAccount;
private LocalDateTime expenseDate;
private String purpose;
@JsonSerialize(using = ToStringSerializer.class)
private Long requestId;
@JsonSerialize(using = ToStringSerializer.class)
private Long projectId;
@JsonSerialize(using = ToStringSerializer.class)
private Long customerId;
private String requestId;
private String projectId;
private String customerId;
private Integer payStatus;
private String payStatusName;
private LocalDateTime payTime;
@ -35,20 +30,17 @@ public class FundExpenseVO {
private String payVoucher;
private Integer approvalStatus;
private String approvalStatusName;
@JsonSerialize(using = ToStringSerializer.class)
private Long approverId;
private String approverId;
private LocalDateTime approvalTime;
private String approvalComment;
private String attachments;
@JsonSerialize(using = ToStringSerializer.class)
private Long tenantId;
@JsonSerialize(using = ToStringSerializer.class)
private Long createdBy;
private String tenantId;
private String createdBy;
private LocalDateTime createdTime;
// getters and setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getExpenseNo() { return expenseNo; }
public void setExpenseNo(String expenseNo) { this.expenseNo = expenseNo; }
public String getTitle() { return title; }
@ -57,8 +49,8 @@ public class FundExpenseVO {
public void setAmount(BigDecimal amount) { this.amount = amount; }
public String getCurrency() { return currency; }
public void setCurrency(String currency) { this.currency = currency; }
public Long getExpenseType() { return expenseType; }
public void setExpenseType(Long expenseType) { this.expenseType = expenseType; }
public String getExpenseType() { return expenseType; }
public void setExpenseType(String expenseType) { this.expenseType = expenseType; }
public String getExpenseTypeName() { return expenseTypeName; }
public void setExpenseTypeName(String expenseTypeName) { this.expenseTypeName = expenseTypeName; }
public String getPayeeName() { return payeeName; }
@ -71,12 +63,12 @@ public class FundExpenseVO {
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 getRequestId() { return requestId; }
public void setRequestId(String requestId) { this.requestId = requestId; }
public String getProjectId() { return projectId; }
public void setProjectId(String projectId) { this.projectId = projectId; }
public String getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; }
public Integer getPayStatus() { return payStatus; }
public void setPayStatus(Integer payStatus) { this.payStatus = payStatus; }
public String getPayStatusName() { return payStatusName; }
@ -91,18 +83,18 @@ public class FundExpenseVO {
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 String getApproverId() { return approverId; }
public void setApproverId(String 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 String getTenantId() { return tenantId; }
public void setTenantId(String tenantId) { this.tenantId = tenantId; }
public String getCreatedBy() { return createdBy; }
public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }
public LocalDateTime getCreatedTime() { return createdTime; }
public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
}

View File

@ -55,10 +55,10 @@ public class FileController {
public Result<FileRecordVO> upload(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "businessType", required = false) String businessType,
@RequestParam(value = "businessId", required = false) Long businessId,
@RequestParam(value = "businessId", required = false) String businessId,
@RequestParam(value = "description", required = false) String description,
@RequestHeader(value = "X-Tenant-Id", required = false) Long tenantId,
@RequestHeader(value = "X-User-Id", required = false) Long userId) {
@RequestHeader(value = "X-Tenant-Id", required = false) String tenantId,
@RequestHeader(value = "X-User-Id", required = false) String userId) {
if (file.isEmpty()) {
return Result.error("请选择要上传的文件");
@ -111,7 +111,7 @@ public class FileController {
// 创建文件记录
FileRecord record = new FileRecord();
record.setTenantId(tenantId != null ? tenantId : 1L);
record.setTenantId(tenantId != null ? tenantId : "default");
record.setFileName(originalFilename);
record.setFilePath(filePath);
record.setFileUrl(fileUrl);
@ -124,7 +124,7 @@ public class FileController {
record.setDescription(description);
record.setCreatedBy(userId);
Long fileId = fileRecordService.createFileRecord(record);
String fileId = fileRecordService.createFileRecord(record);
// 返回VO
FileRecordVO vo = new FileRecordVO();
@ -154,7 +154,7 @@ public class FileController {
*/
@GetMapping("/download/**")
public void download(HttpServletResponse response,
@RequestHeader(value = "X-Tenant-Id", required = false) Long tenantId) throws IOException {
@RequestHeader(value = "X-Tenant-Id", required = false) String tenantId) throws IOException {
String requestUri = request.getRequestURI();
String filePath = requestUri.substring(requestUri.indexOf("/download/") + 10);
String fullPath = uploadPath + "/" + filePath;
@ -194,7 +194,7 @@ public class FileController {
* 根据ID获取文件信息
*/
@GetMapping("/{id}")
public Result<FileRecordVO> getById(@PathVariable Long id) {
public Result<FileRecordVO> getById(@PathVariable String id) {
FileRecord record = fileRecordService.getById(id);
if (record == null) {
return Result.error("文件不存在");
@ -210,7 +210,7 @@ public class FileController {
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize,
@RequestParam(required = false) String businessType,
@RequestParam(required = false) Long businessId,
@RequestParam(required = false) String businessId,
@RequestParam(required = false) String fileType) {
Page<FileRecord> page = fileRecordService.page(pageNum, pageSize, businessType, businessId, fileType);
@ -231,7 +231,7 @@ public class FileController {
@GetMapping("/list")
public Result<List<FileRecordVO>> listByBusiness(
@RequestParam String businessType,
@RequestParam Long businessId) {
@RequestParam String businessId) {
List<FileRecord> records = fileRecordService.listByBusiness(businessType, businessId);
List<FileRecordVO> voList = new ArrayList<>();
@ -245,7 +245,7 @@ public class FileController {
* 删除文件
*/
@DeleteMapping("/{id}")
public Result<Boolean> delete(@PathVariable Long id) {
public Result<Boolean> delete(@PathVariable String id) {
FileRecord record = fileRecordService.getById(id);
if (record == null) {
return Result.error("文件不存在");

View File

@ -28,7 +28,7 @@ public class FileRecordService {
* 创建文件记录
*/
@Transactional
public Long createFileRecord(FileRecord record) {
public String createFileRecord(FileRecord record) {
record.setDownloadCount(0);
record.setStatus(1);
record.setCreatedTime(LocalDateTime.now());
@ -39,14 +39,14 @@ public class FileRecordService {
/**
* 根据ID查询
*/
public FileRecord getById(Long id) {
public FileRecord getById(String id) {
return fileRecordMapper.selectById(id);
}
/**
* 分页查询
*/
public Page<FileRecord> page(int pageNum, int pageSize, String businessType, Long businessId, String fileType) {
public Page<FileRecord> page(int pageNum, int pageSize, String businessType, String businessId, String fileType) {
Page<FileRecord> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<FileRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(businessType != null, FileRecord::getBusinessType, businessType);
@ -59,7 +59,7 @@ public class FileRecordService {
/**
* 根据业务类型和ID查询
*/
public List<FileRecord> listByBusiness(String businessType, Long businessId) {
public List<FileRecord> listByBusiness(String businessType, String businessId) {
LambdaQueryWrapper<FileRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(FileRecord::getBusinessType, businessType);
wrapper.eq(FileRecord::getBusinessId, businessId);
@ -80,7 +80,7 @@ public class FileRecordService {
* 增加下载次数
*/
@Transactional
public boolean incrementDownloadCount(Long id) {
public boolean incrementDownloadCount(String id) {
FileRecord record = fileRecordMapper.selectById(id);
if (record != null) {
record.setDownloadCount(record.getDownloadCount() + 1);
@ -93,7 +93,7 @@ public class FileRecordService {
* 删除文件记录
*/
@Transactional
public boolean deleteFileRecord(Long id) {
public boolean deleteFileRecord(String id) {
return fileRecordMapper.deleteById(id) > 0;
}

View File

@ -7,9 +7,9 @@ import java.time.LocalDateTime;
*/
public class FileRecordVO {
private Long fileId;
private String fileId;
private Long tenantId;
private String tenantId;
private String fileName;
@ -27,7 +27,7 @@ public class FileRecordVO {
private String businessType;
private Long businessId;
private String businessId;
private String description;
@ -35,25 +35,25 @@ public class FileRecordVO {
private Integer status;
private Long createdBy;
private String createdBy;
private String createdByName;
private LocalDateTime createdTime;
public Long getFileId() {
public String getFileId() {
return fileId;
}
public void setFileId(Long fileId) {
public void setFileId(String fileId) {
this.fileId = fileId;
}
public Long getTenantId() {
public String getTenantId() {
return tenantId;
}
public void setTenantId(Long tenantId) {
public void setTenantId(String tenantId) {
this.tenantId = tenantId;
}
@ -121,11 +121,11 @@ public class FileRecordVO {
this.businessType = businessType;
}
public Long getBusinessId() {
public String getBusinessId() {
return businessId;
}
public void setBusinessId(Long businessId) {
public void setBusinessId(String businessId) {
this.businessId = businessId;
}
@ -153,11 +153,11 @@ public class FileRecordVO {
this.status = status;
}
public Long getCreatedBy() {
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Long createdBy) {
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}

View File

@ -24,7 +24,7 @@ public class RequirementController {
*/
@GetMapping("/page")
public Result<Page<RequirementVO>> page(
@RequestHeader(value = "X-Tenant-Id", required = false) Long tenantId,
@RequestHeader(value = "X-Tenant-Id", required = false) String tenantId,
@RequestParam(required = false) String requirementName,
@RequestParam(required = false) String projectName,
@RequestParam(required = false) String requirementStatus,
@ -39,8 +39,8 @@ public class RequirementController {
*/
@GetMapping("/{requirementId}")
public Result<RequirementVO> get(
@RequestHeader("X-Tenant-Id") Long tenantId,
@PathVariable Long requirementId) {
@RequestHeader("X-Tenant-Id") String tenantId,
@PathVariable String requirementId) {
return requirementService.getById(tenantId, requirementId);
}

View File

@ -11,7 +11,7 @@ import java.time.LocalDate;
*/
public class RequirementDTO {
private Long requirementId;
private String requirementId;
@NotBlank(message = "需求编号不能为空")
private String requirementCode;
@ -21,11 +21,11 @@ public class RequirementDTO {
private String description;
@NotNull(message = "项目ID不能为空")
private Long projectId;
@NotBlank(message = "项目ID不能为空")
private String projectId;
@NotNull(message = "客户ID不能为空")
private Long customerId;
@NotBlank(message = "客户ID不能为空")
private String customerId;
private String priority;
@ -53,11 +53,11 @@ public class RequirementDTO {
private String attachmentUrl;
public Long getRequirementId() {
public String getRequirementId() {
return requirementId;
}
public void setRequirementId(Long requirementId) {
public void setRequirementId(String requirementId) {
this.requirementId = requirementId;
}
@ -85,19 +85,19 @@ public class RequirementDTO {
this.description = description;
}
public Long getProjectId() {
public String getProjectId() {
return projectId;
}
public void setProjectId(Long projectId) {
public void setProjectId(String projectId) {
this.projectId = projectId;
}
public Long getCustomerId() {
public String getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
public void setCustomerId(String customerId) {
this.customerId = customerId;
}

View File

@ -47,8 +47,8 @@ public class RequirementService {
/**
* 分页查询需求工单
*/
public Result<Page<RequirementVO>> page(Long tenantId, String requirementName, String status,
Long projectId, Long customerId, int current, int size) {
public Result<Page<RequirementVO>> page(String tenantId, String requirementName, String status,
String projectId, String customerId, int current, int size) {
Page<Requirement> page = new Page<>(current, size);
LambdaQueryWrapper<Requirement> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Requirement::getTenantId, tenantId);
@ -80,7 +80,7 @@ public class RequirementService {
/**
* 查询需求工单详情
*/
public Result<RequirementVO> getById(Long tenantId, Long requirementId) {
public Result<RequirementVO> getById(String tenantId, String requirementId) {
Requirement requirement = requirementDataService.getOne(
new LambdaQueryWrapper<Requirement>()
.eq(Requirement::getId, requirementId)

View File

@ -8,22 +8,22 @@ import java.util.List;
public interface FundReceiptService {
Long createReceipt(FundReceiptDTO dto);
String createReceipt(FundReceiptDTO dto);
boolean updateReceipt(FundReceiptDTO dto);
FundReceiptVO getReceiptById(Long id);
FundReceiptVO getReceiptById(String id);
Page<FundReceiptVO> pageReceipts(int pageNum, int pageSize, String title, Integer receiptType, Integer receiptStatus);
/**
* 根据应收款ID查询收款记录
*/
List<FundReceiptVO> getReceiptsByReceivableId(Long receivableId);
List<FundReceiptVO> getReceiptsByReceivableId(String receivableId);
boolean deleteReceipt(Long id);
boolean deleteReceipt(String id);
boolean confirm(Long id);
boolean confirm(String id);
boolean writeOff(Long id);
boolean writeOff(String id);
}

View File

@ -16,7 +16,7 @@ public interface ReceivableService {
/**
* 创建应收款
*/
Long createReceivable(ReceivableDTO dto);
String createReceivable(ReceivableDTO dto);
/**
* 更新应收款
@ -26,37 +26,37 @@ public interface ReceivableService {
/**
* 根据ID查询应收款
*/
ReceivableVO getReceivableById(Long id);
ReceivableVO getReceivableById(String id);
/**
* 分页查询应收款
*/
Page<ReceivableVO> pageReceivables(int pageNum, int pageSize, Long projectId, Long customerId, String status, Integer confirmStatus);
Page<ReceivableVO> pageReceivables(int pageNum, int pageSize, String projectId, String customerId, String status, Integer confirmStatus);
/**
* 查询应收款列表不分页用于导出
*/
List<ReceivableVO> listReceivables(Long projectId, Long customerId, String status, Integer confirmStatus);
List<ReceivableVO> listReceivables(String projectId, String customerId, String status, Integer confirmStatus);
/**
* 确认应收款
*/
boolean confirmReceivable(Long id, Long confirmBy);
boolean confirmReceivable(String id, String confirmBy);
/**
* 取消确认
*/
boolean cancelConfirm(Long id);
boolean cancelConfirm(String id);
/**
* 记录收款更新已收款金额
*/
boolean recordReceipt(Long id, BigDecimal amount);
boolean recordReceipt(String id, BigDecimal amount);
/**
* 获取应收款的收款记录
*/
List<FundReceiptVO> getReceiptsByReceivableId(Long receivableId);
List<FundReceiptVO> getReceiptsByReceivableId(String receivableId);
/**
* 更新逾期状态
@ -66,7 +66,7 @@ public interface ReceivableService {
/**
* 删除应收款
*/
boolean deleteReceivable(Long id);
boolean deleteReceivable(String id);
// ===================== 统计相关 =====================

View File

@ -34,7 +34,7 @@ public class FundReceiptServiceImpl implements FundReceiptService {
@Override
@Transactional(rollbackFor = Exception.class)
public Long createReceipt(FundReceiptDTO dto) {
public String createReceipt(FundReceiptDTO dto) {
FundReceipt receipt = new FundReceipt();
receipt.setReceiptNo(generateReceiptNo());
receipt.setTitle(dto.getTitle());
@ -80,7 +80,7 @@ public class FundReceiptServiceImpl implements FundReceiptService {
}
@Override
public FundReceiptVO getReceiptById(Long id) {
public FundReceiptVO getReceiptById(String id) {
FundReceipt receipt = receiptDataService.getById(id);
if (receipt == null || receipt.getDeleted() == 1) throw new RuntimeException("收款记录不存在");
return convertToVO(receipt);
@ -103,7 +103,7 @@ public class FundReceiptServiceImpl implements FundReceiptService {
}
@Override
public List<FundReceiptVO> getReceiptsByReceivableId(Long receivableId) {
public List<FundReceiptVO> getReceiptsByReceivableId(String receivableId) {
LambdaQueryWrapper<FundReceipt> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(FundReceipt::getDeleted, 0);
wrapper.eq(FundReceipt::getReceivableId, receivableId);
@ -113,7 +113,7 @@ public class FundReceiptServiceImpl implements FundReceiptService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteReceipt(Long id) {
public boolean deleteReceipt(String id) {
LambdaUpdateWrapper<FundReceipt> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(FundReceipt::getId, id).set(FundReceipt::getDeleted, 1).set(FundReceipt::getUpdatedTime, LocalDateTime.now());
return receiptDataService.update(wrapper);
@ -121,7 +121,7 @@ public class FundReceiptServiceImpl implements FundReceiptService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean confirm(Long id) {
public boolean confirm(String id) {
LambdaUpdateWrapper<FundReceipt> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(FundReceipt::getId, id).set(FundReceipt::getReceiptStatus, 1)
.set(FundReceipt::getConfirmTime, LocalDateTime.now())
@ -131,7 +131,7 @@ public class FundReceiptServiceImpl implements FundReceiptService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean writeOff(Long id) {
public boolean writeOff(String id) {
LambdaUpdateWrapper<FundReceipt> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(FundReceipt::getId, id).set(FundReceipt::getReceiptStatus, 2)
.set(FundReceipt::getWriteOffTime, LocalDateTime.now())

View File

@ -54,7 +54,7 @@ public class ReceivableServiceImpl implements ReceivableService {
@Override
@Transactional(rollbackFor = Exception.class)
public Long createReceivable(ReceivableDTO dto) {
public String createReceivable(ReceivableDTO dto) {
Receivable receivable = new Receivable();
receivable.setReceivableCode(generateReceivableCode());
receivable.setRequirementId(dto.getRequirementId());
@ -112,7 +112,7 @@ public class ReceivableServiceImpl implements ReceivableService {
}
@Override
public ReceivableVO getReceivableById(Long id) {
public ReceivableVO getReceivableById(String id) {
Receivable receivable = receivableDataService.getById(id);
if (receivable == null || receivable.getDeleted() == 1) {
throw new RuntimeException("应收款不存在");
@ -121,7 +121,7 @@ public class ReceivableServiceImpl implements ReceivableService {
}
@Override
public Page<ReceivableVO> pageReceivables(int pageNum, int pageSize, Long projectId, Long customerId, String status, Integer confirmStatus) {
public Page<ReceivableVO> pageReceivables(int pageNum, int pageSize, String projectId, String customerId, String status, Integer confirmStatus) {
Page<Receivable> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<Receivable> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Receivable::getDeleted, 0);
@ -138,7 +138,7 @@ public class ReceivableServiceImpl implements ReceivableService {
}
@Override
public List<ReceivableVO> listReceivables(Long projectId, Long customerId, String status, Integer confirmStatus) {
public List<ReceivableVO> listReceivables(String projectId, String customerId, String status, Integer confirmStatus) {
LambdaQueryWrapper<Receivable> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Receivable::getDeleted, 0);
if (projectId != null) wrapper.eq(Receivable::getProjectId, projectId);
@ -152,7 +152,7 @@ public class ReceivableServiceImpl implements ReceivableService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean confirmReceivable(Long id, Long confirmBy) {
public boolean confirmReceivable(String id, String confirmBy) {
Receivable existing = receivableDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("应收款不存在");
@ -178,7 +178,7 @@ public class ReceivableServiceImpl implements ReceivableService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean cancelConfirm(Long id) {
public boolean cancelConfirm(String id) {
Receivable existing = receivableDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("应收款不存在");
@ -207,7 +207,7 @@ public class ReceivableServiceImpl implements ReceivableService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean recordReceipt(Long id, BigDecimal amount) {
public boolean recordReceipt(String id, BigDecimal amount) {
Receivable existing = receivableDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("应收款不存在");
@ -248,7 +248,7 @@ public class ReceivableServiceImpl implements ReceivableService {
}
@Override
public List<FundReceiptVO> getReceiptsByReceivableId(Long receivableId) {
public List<FundReceiptVO> getReceiptsByReceivableId(String receivableId) {
return receiptService.getReceiptsByReceivableId(receivableId);
}
@ -279,7 +279,7 @@ public class ReceivableServiceImpl implements ReceivableService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteReceivable(Long id) {
public boolean deleteReceivable(String id) {
Receivable existing = receivableDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("应收款不存在");

View File

@ -9,12 +9,12 @@ import java.time.LocalDateTime;
*/
public class ReceivableVO {
private Long id;
private String id;
private String receivableCode;
private Long requirementId;
private Long projectId;
private String requirementId;
private String projectId;
private String projectName;
private Long customerId;
private String customerId;
private String customerName;
private BigDecimal receivableAmount;
private BigDecimal receivedAmount;
@ -30,18 +30,18 @@ public class ReceivableVO {
private Integer confirmStatus;
private String confirmStatusName;
private LocalDateTime confirmTime;
private Long confirmBy;
private Long tenantId;
private Long createdBy;
private String confirmBy;
private String tenantId;
private String createdBy;
private LocalDateTime createdTime;
private LocalDateTime updatedTime;
private String remark;
public Long getId() {
public String getId() {
return id;
}
public void setId(Long id) {
public void setId(String id) {
this.id = id;
}
@ -53,19 +53,19 @@ public class ReceivableVO {
this.receivableCode = receivableCode;
}
public Long getRequirementId() {
public String getRequirementId() {
return requirementId;
}
public void setRequirementId(Long requirementId) {
public void setRequirementId(String requirementId) {
this.requirementId = requirementId;
}
public Long getProjectId() {
public String getProjectId() {
return projectId;
}
public void setProjectId(Long projectId) {
public void setProjectId(String projectId) {
this.projectId = projectId;
}
@ -77,11 +77,11 @@ public class ReceivableVO {
this.projectName = projectName;
}
public Long getCustomerId() {
public String getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
@ -205,27 +205,27 @@ public class ReceivableVO {
this.confirmTime = confirmTime;
}
public Long getConfirmBy() {
public String getConfirmBy() {
return confirmBy;
}
public void setConfirmBy(Long confirmBy) {
public void setConfirmBy(String confirmBy) {
this.confirmBy = confirmBy;
}
public Long getTenantId() {
public String getTenantId() {
return tenantId;
}
public void setTenantId(Long tenantId) {
public void setTenantId(String tenantId) {
this.tenantId = tenantId;
}
public Long getCreatedBy() {
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Long createdBy) {
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}

View File

@ -66,8 +66,8 @@ public class ReportController {
@GetMapping("/project/finance")
public Result<List<ProjectFinanceDTO>> getProjectFinance(
@RequestParam(required = false) String status,
@RequestParam(required = false) Long customerId,
@RequestHeader(value = "X-Tenant-Id", required = false) Long tenantId) {
@RequestParam(required = false) String customerId,
@RequestHeader(value = "X-Tenant-Id", required = false) String tenantId) {
return Result.success(reportService.getProjectFinance(tenantId, status, customerId));
}
@ -77,8 +77,8 @@ public class ReportController {
@GetMapping("/project/finance/export")
public void exportProjectFinance(
@RequestParam(required = false) String status,
@RequestParam(required = false) Long customerId,
@RequestHeader(value = "X-Tenant-Id", required = false) Long tenantId,
@RequestParam(required = false) String customerId,
@RequestHeader(value = "X-Tenant-Id", required = false) String tenantId,
HttpServletResponse response) {
List<ProjectFinanceDTO> data = reportService.getProjectFinance(tenantId, status, customerId);
List<ProjectFinanceExcel> excelData = convertToExcel(data);

View File

@ -8,7 +8,7 @@ import java.math.BigDecimal;
public class ProjectFinanceDTO {
/** 项目ID */
private Long projectId;
private String projectId;
/** 项目编号 */
private String projectCode;
@ -43,11 +43,11 @@ public class ProjectFinanceDTO {
/** 利润率 */
private BigDecimal profitRate;
public Long getProjectId() {
public String getProjectId() {
return projectId;
}
public void setProjectId(Long projectId) {
public void setProjectId(String projectId) {
this.projectId = projectId;
}

View File

@ -26,7 +26,7 @@ public interface ProjectFeignClient {
*/
@GetMapping("/api/v1/proj/project/stats/finance")
Result<List<Map<String, Object>>> getProjectsWithFinance(
@RequestHeader(value = "X-Tenant-Id", required = false) Long tenantId,
@RequestHeader(value = "X-Tenant-Id", required = false) String tenantId,
@RequestParam(required = false) String status,
@RequestParam(required = false) Long customerId);
@RequestParam(required = false) String customerId);
}

View File

@ -36,5 +36,5 @@ public interface ReportService {
/**
* 获取项目收支分析报表
*/
List<ProjectFinanceDTO> getProjectFinance(Long tenantId, String status, Long customerId);
List<ProjectFinanceDTO> getProjectFinance(String tenantId, String status, String customerId);
}

View File

@ -211,7 +211,7 @@ public class ReportServiceImpl implements ReportService {
}
@Override
public List<ProjectFinanceDTO> getProjectFinance(Long tenantId, String status, Long customerId) {
public List<ProjectFinanceDTO> getProjectFinance(String tenantId, String status, String customerId) {
List<ProjectFinanceDTO> result = new ArrayList<>();
try {
@ -220,7 +220,7 @@ public class ReportServiceImpl implements ReportService {
if (projectsResult != null && projectsResult.getData() != null) {
for (Map<String, Object> item : projectsResult.getData()) {
ProjectFinanceDTO dto = new ProjectFinanceDTO();
dto.setProjectId(((Number) item.get("projectId")).longValue());
dto.setProjectId(String.valueOf(item.get("projectId")));
dto.setProjectCode((String) item.get("projectCode"));
dto.setProjectName((String) item.get("projectName"));
dto.setCustomerName((String) item.get("customerName"));
@ -262,15 +262,15 @@ public class ReportServiceImpl implements ReportService {
} catch (Exception e) {
log.warn("获取项目收支分析失败: {}", e.getMessage());
// 返回模拟数据
result.add(createMockProjectFinance(1L, "PRJ-001", "项目A", "客户A", "progress"));
result.add(createMockProjectFinance(2L, "PRJ-002", "项目B", "客户B", "completed"));
result.add(createMockProjectFinance(3L, "PRJ-003", "项目C", "客户C", "progress"));
result.add(createMockProjectFinance("1", "PRJ-001", "项目A", "客户A", "progress"));
result.add(createMockProjectFinance("2", "PRJ-002", "项目B", "客户B", "completed"));
result.add(createMockProjectFinance("3", "PRJ-003", "项目C", "客户C", "progress"));
}
return result;
}
private ProjectFinanceDTO createMockProjectFinance(Long id, String code, String name, String customer, String status) {
private ProjectFinanceDTO createMockProjectFinance(String id, String code, String name, String customer, String status) {
ProjectFinanceDTO dto = new ProjectFinanceDTO();
dto.setProjectId(id);
dto.setProjectCode(code);

View File

@ -31,7 +31,7 @@ public class OperationLogController {
public Result<Page<OperationLog>> page(
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum,
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize,
@Parameter(description = "用户ID") @RequestParam(required = false) Long userId,
@Parameter(description = "用户ID") @RequestParam(required = false) String userId,
@Parameter(description = "操作类型") @RequestParam(required = false) String operation,
@Parameter(description = "开始时间yyyy-MM-dd HH:mm:ss") @RequestParam(required = false) String startTime,
@Parameter(description = "结束时间yyyy-MM-dd HH:mm:ss") @RequestParam(required = false) String endTime) {

View File

@ -12,9 +12,9 @@ import java.time.LocalDateTime;
@TableName("sys_tenant")
public class SysTenant extends BaseEntity {
/** 租户表不需要tenant_id字段 */
/** 租户表不需要tenant_id字段但需要覆盖父类的getter方法以避免返回null */
@TableField(exist = false)
private String tenantId;
private String tenantId = "master";
public String getTenantId() {
return tenantId;

View File

@ -16,7 +16,7 @@ public interface OperationLogService {
/**
* 分页查询操作日志
*/
Page<OperationLog> pageLogs(int pageNum, int pageSize, Long userId, String operation, String startTime, String endTime);
Page<OperationLog> pageLogs(int pageNum, int pageSize, String userId, String operation, String startTime, String endTime);
/**
* 根据ID查询日志详情

View File

@ -34,7 +34,7 @@ public class OperationLogServiceImpl implements OperationLogService {
}
@Override
public Page<OperationLog> pageLogs(int pageNum, int pageSize, Long userId, String operation, String startTime, String endTime) {
public Page<OperationLog> pageLogs(int pageNum, int pageSize, String userId, String operation, String startTime, String endTime) {
Page<OperationLog> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<OperationLog> wrapper = new LambdaQueryWrapper<>();

323
scripts/dev-start.sh Executable file
View File

@ -0,0 +1,323 @@
#!/bin/bash
# ============================================
# 本地开发环境启动脚本
# 用法: ./dev-start.sh [选项]
# 选项:
# all - 启动所有服务(默认)
# core - 启动核心服务gateway + sys + frontend
# gateway - 仅启动网关
# sys - 仅启动系统服务
# cust - 仅启动客户服务
# proj - 仅启动项目服务
# req - 仅启动需求服务
# exp - 仅启动支出服务
# receipt - 仅启动票据服务
# file - 仅启动文件服务
# report - 仅启动报表服务
# frontend - 仅启动前端
# help - 显示帮助信息
# ============================================
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
APP_HOME="$(dirname "$SCRIPT_DIR")"
LOG_DIR="${APP_HOME}/logs"
# 创建日志目录
mkdir -p "${LOG_DIR}"
# 服务端口配置
GATEWAY_PORT=8000
SYS_PORT=8100
CUST_PORT=8110
PROJ_PORT=8120
REQ_PORT=8130
EXP_PORT=8140
RECEIPT_PORT=8150
FILE_PORT=8600
REPORT_PORT=8700
FRONTEND_PORT=3000
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查端口是否被占用
check_port() {
local port=$1
if netstat -tlnp 2>/dev/null | grep -q ":${port} "; then
return 0 # 端口被占用
fi
return 1 # 端口空闲
}
# 通用服务启动函数
# 参数: $1=服务名, $2=模块名, $3=端口, $4=主类名
start_service() {
local name=$1
local module=$2
local port=$3
local main_class=$4
log_info "启动 ${name} 服务 (端口: ${port})..."
if check_port ${port}; then
log_warn "端口 ${port} 已被占用,跳过 ${name} 启动"
return 0
fi
cd "${APP_HOME}"
nohup mvn -pl ${module} exec:java \
-Dexec.mainClass="${main_class}" \
> "${LOG_DIR}/${name}-console.log" 2>&1 &
local pid=$!
echo ${pid} > "${LOG_DIR}/${name}.pid"
log_info "${name} 启动中... PID: ${pid}"
# 等待启动完成
sleep 3
if check_port ${port}; then
log_info "${name} 启动成功!"
else
log_warn "${name} 可能还在启动中,请检查日志: ${LOG_DIR}/${name}-console.log"
fi
}
# 启动网关服务
start_gateway() {
start_service "gateway" "fund-gateway" ${GATEWAY_PORT} "com.fundplatform.gateway.GatewayApplication"
}
# 启动系统服务
start_sys() {
start_service "sys" "fund-sys" ${SYS_PORT} "com.fundplatform.sys.SysApplication"
}
# 启动客户服务
start_cust() {
start_service "cust" "fund-cust" ${CUST_PORT} "com.fundplatform.cust.CustApplication"
}
# 启动项目服务
start_proj() {
start_service "proj" "fund-proj" ${PROJ_PORT} "com.fundplatform.proj.ProjApplication"
}
# 启动需求服务
start_req() {
start_service "req" "fund-req" ${REQ_PORT} "com.fundplatform.req.ReqApplication"
}
# 启动支出服务
start_exp() {
start_service "exp" "fund-exp" ${EXP_PORT} "com.fundplatform.exp.ExpApplication"
}
# 启动票据服务
start_receipt() {
start_service "receipt" "fund-receipt" ${RECEIPT_PORT} "com.fundplatform.receipt.ReceiptApplication"
}
# 启动文件服务
start_file() {
start_service "file" "fund-file" ${FILE_PORT} "com.fundplatform.file.FileApplication"
}
# 启动报表服务
start_report() {
start_service "report" "fund-report" ${REPORT_PORT} "com.fundplatform.report.ReportApplication"
}
# 启动前端服务
start_frontend() {
log_info "启动管理后台前端 (端口: ${FRONTEND_PORT})..."
cd "${APP_HOME}/fund-admin"
# 检查 node_modules 是否存在
if [ ! -d "node_modules" ]; then
log_info "安装前端依赖..."
npm install
fi
# 查找可用端口
local port=${FRONTEND_PORT}
while check_port ${port}; do
port=$((port + 1))
if [ ${port} -gt 3010 ]; then
log_error "无法找到可用端口 (3000-3010)"
return 1
fi
done
if [ ${port} -ne ${FRONTEND_PORT} ]; then
log_warn "端口 ${FRONTEND_PORT} 已被占用,使用端口 ${port}"
fi
nohup npm run dev -- --port ${port} --host 0.0.0.0 \
> "${LOG_DIR}/frontend-console.log" 2>&1 &
local pid=$!
echo ${pid} > "${LOG_DIR}/frontend.pid"
log_info "前端启动中... PID: ${pid}"
sleep 3
log_info "前端启动成功!访问地址: http://localhost:${port}"
}
# 启动核心服务
start_core() {
log_info "=========================================="
log_info "启动核心服务..."
log_info "=========================================="
start_gateway
start_sys
start_frontend
log_info "=========================================="
log_info "核心服务启动完成!"
log_info "=========================================="
}
# 启动所有服务
start_all() {
log_info "=========================================="
log_info "启动本地开发环境(全部服务)..."
log_info "=========================================="
# 检查 Nacos
if ! check_port 8848; then
log_warn "Nacos (端口8848) 未运行,部分服务可能启动失败"
log_warn "请先启动 Nacos 服务"
fi
# 检查 MySQL
if ! check_port 3306; then
log_warn "MySQL (端口3306) 未运行,数据库相关服务将启动失败"
log_warn "请先启动 MySQL 服务"
fi
start_gateway
start_sys
start_cust
start_proj
start_req
start_exp
start_receipt
start_file
start_report
start_frontend
log_info "=========================================="
log_info "本地开发环境启动完成!"
log_info "=========================================="
echo ""
echo "服务地址:"
echo " 网关: http://localhost:${GATEWAY_PORT}"
echo " 系统: http://localhost:${SYS_PORT}"
echo " 客户: http://localhost:${CUST_PORT}"
echo " 项目: http://localhost:${PROJ_PORT}"
echo " 需求: http://localhost:${REQ_PORT}"
echo " 支出: http://localhost:${EXP_PORT}"
echo " 票据: http://localhost:${RECEIPT_PORT}"
echo " 文件: http://localhost:${FILE_PORT}"
echo " 报表: http://localhost:${REPORT_PORT}"
echo " 前端: http://localhost:${FRONTEND_PORT} (或查看日志获取实际端口)"
echo ""
echo "日志目录: ${LOG_DIR}"
echo "停止服务: ./scripts/dev-stop.sh"
}
# 显示帮助
show_help() {
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " all 启动所有服务(默认)"
echo " core 启动核心服务gateway + sys + frontend"
echo " gateway 仅启动网关服务"
echo " sys 仅启动系统服务"
echo " cust 仅启动客户服务"
echo " proj 仅启动项目服务"
echo " req 仅启动需求服务"
echo " exp 仅启动支出服务"
echo " receipt 仅启动票据服务"
echo " file 仅启动文件服务"
echo " report 仅启动报表服务"
echo " frontend 仅启动前端服务"
echo " help 显示此帮助信息"
echo ""
echo "服务端口:"
echo " gateway: ${GATEWAY_PORT} | sys: ${SYS_PORT} | cust: ${CUST_PORT}"
echo " proj: ${PROJ_PORT} | req: ${REQ_PORT} | exp: ${EXP_PORT}"
echo " receipt: ${RECEIPT_PORT} | file: ${FILE_PORT} | report: ${REPORT_PORT}"
echo ""
echo "示例:"
echo " $0 # 启动所有服务"
echo " $0 core # 启动核心服务(网关+系统+前端)"
echo " $0 sys cust # 启动系统和客户服务"
echo " $0 frontend # 仅启动前端"
}
# 主入口
case "$1" in
gateway)
start_gateway
;;
sys)
start_sys
;;
cust)
start_cust
;;
proj)
start_proj
;;
req)
start_req
;;
exp)
start_exp
;;
receipt)
start_receipt
;;
file)
start_file
;;
report)
start_report
;;
frontend)
start_frontend
;;
core)
start_core
;;
help|--help|-h)
show_help
;;
all|"")
start_all
;;
*)
log_error "未知选项: $1"
show_help
exit 1
;;
esac

138
scripts/dev-status.sh Executable file
View File

@ -0,0 +1,138 @@
#!/bin/bash
# ============================================
# 本地开发环境状态检查脚本
# 用法: ./dev-status.sh
# ============================================
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 服务端口配置
GATEWAY_PORT=8000
SYS_PORT=8100
CUST_PORT=8110
PROJ_PORT=8120
REQ_PORT=8130
EXP_PORT=8140
RECEIPT_PORT=8150
FILE_PORT=8600
REPORT_PORT=8700
# 检查端口状态
check_port_status() {
local port=$1
local service=$2
local pid=""
if netstat -tlnp 2>/dev/null | grep -q ":${port} "; then
pid=$(netstat -tlnp 2>/dev/null | grep ":${port} " | awk '{print $7}' | cut -d'/' -f1)
echo -e " ${GREEN}${NC} ${service} (端口 ${port}, PID: ${pid})"
return 0
else
echo -e " ${RED}${NC} ${service} (端口 ${port})"
return 1
fi
}
# 检查前端状态
check_frontend_status() {
local vite_pids=$(pgrep -f "vite.*fund-admin" | head -1)
if [ -n "${vite_pids}" ]; then
local port=$(netstat -tlnp 2>/dev/null | grep "${vite_pids}/node" | awk '{print $4}' | grep -oP ':\d+$' | tr -d ':')
echo -e " ${GREEN}${NC} 前端 (fund-admin) - 端口: ${port:-未知}, PID: ${vite_pids}"
echo -e " 访问地址: http://localhost:${port:-3000}"
else
echo -e " ${RED}${NC} 前端 (fund-admin)"
fi
}
# 检查依赖服务
check_dependencies() {
echo ""
echo -e "${BLUE}依赖服务:${NC}"
# Nacos
if netstat -tlnp 2>/dev/null | grep -q ":8848 "; then
echo -e " ${GREEN}${NC} Nacos (8848)"
else
echo -e " ${RED}${NC} Nacos (8848) - 未运行"
fi
# MySQL
if netstat -tlnp 2>/dev/null | grep -q ":3306 "; then
echo -e " ${GREEN}${NC} MySQL (3306)"
else
echo -e " ${RED}${NC} MySQL (3306) - 未运行"
fi
# Redis
if netstat -tlnp 2>/dev/null | grep -q ":6379 "; then
echo -e " ${GREEN}${NC} Redis (6379)"
else
echo -e " ${YELLOW}?${NC} Redis (6379) - 未运行 (可选)"
fi
}
# 统计运行中的服务数量
count_running_services() {
local count=0
for port in ${GATEWAY_PORT} ${SYS_PORT} ${CUST_PORT} ${PROJ_PORT} ${REQ_PORT} ${EXP_PORT} ${RECEIPT_PORT} ${FILE_PORT} ${REPORT_PORT}; do
if netstat -tlnp 2>/dev/null | grep -q ":${port} "; then
((count++))
fi
done
echo ${count}
}
# 主函数
main() {
echo ""
echo -e "${BLUE}==========================================${NC}"
echo -e "${BLUE} 本地开发环境状态${NC}"
echo -e "${BLUE}==========================================${NC}"
echo ""
echo -e "${BLUE}后端服务:${NC}"
check_port_status ${GATEWAY_PORT} "网关 (fund-gateway)"
check_port_status ${SYS_PORT} "系统 (fund-sys)"
check_port_status ${CUST_PORT} "客户 (fund-cust)"
check_port_status ${PROJ_PORT} "项目 (fund-proj)"
check_port_status ${REQ_PORT} "需求 (fund-req)"
check_port_status ${EXP_PORT} "支出 (fund-exp)"
check_port_status ${RECEIPT_PORT} "票据 (fund-receipt)"
check_port_status ${FILE_PORT} "文件 (fund-file)"
check_port_status ${REPORT_PORT} "报表 (fund-report)"
echo ""
echo -e "${BLUE}前端服务:${NC}"
check_frontend_status
local running=$(count_running_services)
echo ""
echo -e "已启动 ${GREEN}${running}${NC}/9 个后端服务"
check_dependencies
echo ""
echo -e "${BLUE}==========================================${NC}"
echo -e "操作命令:"
echo -e " 启动所有: ${GREEN}./scripts/dev-start.sh${NC}"
echo -e " 启动核心: ${GREEN}./scripts/dev-start.sh core${NC}"
echo -e " 停止所有: ${GREEN}./scripts/dev-stop.sh${NC}"
echo -e " 查看状态: ${GREEN}./scripts/dev-status.sh${NC}"
echo ""
echo -e "单独启动服务:"
echo -e " ${GREEN}./scripts/dev-start.sh sys${NC} # 系统服务"
echo -e " ${GREEN}./scripts/dev-start.sh cust${NC} # 客户服务"
echo -e " ${GREEN}./scripts/dev-start.sh proj${NC} # 项目服务"
echo -e " ${GREEN}./scripts/dev-start.sh frontend${NC} # 前端服务"
echo -e "${BLUE}==========================================${NC}"
echo ""
}
main

237
scripts/dev-stop.sh Executable file
View File

@ -0,0 +1,237 @@
#!/bin/bash
# ============================================
# 本地开发环境停止脚本
# 用法: ./dev-stop.sh [选项]
# 选项:
# all - 停止所有服务(默认)
# gateway - 仅停止网关
# sys - 仅停止系统服务
# cust - 仅停止客户服务
# proj - 仅停止项目服务
# req - 仅停止需求服务
# exp - 仅停止支出服务
# receipt - 仅停止票据服务
# file - 仅停止文件服务
# report - 仅停止报表服务
# frontend - 仅停止前端
# help - 显示帮助信息
# ============================================
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
APP_HOME="$(dirname "$SCRIPT_DIR")"
LOG_DIR="${APP_HOME}/logs"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 停止服务
# 参数: $1=服务名, $2=进程匹配模式
stop_service() {
local name=$1
local pattern=$2
local pid_file="${LOG_DIR}/${name}.pid"
if [ -f "${pid_file}" ]; then
local pid=$(cat "${pid_file}")
if ps -p ${pid} > /dev/null 2>&1; then
log_info "停止 ${name} 服务 (PID: ${pid})..."
kill ${pid} 2>/dev/null
# 等待进程结束
local count=0
while ps -p ${pid} > /dev/null 2>&1; do
if [ $count -ge 10 ]; then
log_warn "${name} 未响应,强制终止..."
kill -9 ${pid} 2>/dev/null
break
fi
sleep 1
((count++))
done
log_info "${name} 服务已停止"
else
log_warn "${name} 服务未运行"
fi
rm -f "${pid_file}"
else
# 通过进程名查找
local pids=$(pgrep -f "${pattern}")
if [ -n "${pids}" ]; then
log_info "停止 ${name} 服务 (PIDs: ${pids})..."
echo "${pids}" | xargs kill 2>/dev/null
log_info "${name} 服务已停止"
else
log_warn "${name} 服务未运行"
fi
fi
}
# 停止网关
stop_gateway() {
stop_service "gateway" "fund-gateway.*GatewayApplication"
}
# 停止系统服务
stop_sys() {
stop_service "sys" "fund-sys.*SysApplication"
}
# 停止客户服务
stop_cust() {
stop_service "cust" "fund-cust.*CustApplication"
}
# 停止项目服务
stop_proj() {
stop_service "proj" "fund-proj.*ProjApplication"
}
# 停止需求服务
stop_req() {
stop_service "req" "fund-req.*ReqApplication"
}
# 停止支出服务
stop_exp() {
stop_service "exp" "fund-exp.*ExpApplication"
}
# 停止票据服务
stop_receipt() {
stop_service "receipt" "fund-receipt.*ReceiptApplication"
}
# 停止文件服务
stop_file() {
stop_service "file" "fund-file.*FileApplication"
}
# 停止报表服务
stop_report() {
stop_service "report" "fund-report.*ReportApplication"
}
# 停止前端
stop_frontend() {
log_info "停止前端服务..."
# 停止 vite 进程
local vite_pids=$(pgrep -f "vite.*fund-admin")
if [ -n "${vite_pids}" ]; then
log_info "停止前端服务 (PIDs: ${vite_pids})..."
echo "${vite_pids}" | xargs kill 2>/dev/null
log_info "前端服务已停止"
else
log_warn "前端服务未运行"
fi
# 清理 PID 文件
rm -f "${LOG_DIR}/frontend.pid"
}
# 停止所有服务
stop_all() {
log_info "=========================================="
log_info "停止本地开发环境..."
log_info "=========================================="
stop_frontend
stop_gateway
stop_sys
stop_cust
stop_proj
stop_req
stop_exp
stop_receipt
stop_file
stop_report
log_info "=========================================="
log_info "本地开发环境已停止"
log_info "=========================================="
}
# 显示帮助
show_help() {
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " all 停止所有服务(默认)"
echo " gateway 仅停止网关服务"
echo " sys 仅停止系统服务"
echo " cust 仅停止客户服务"
echo " proj 仅停止项目服务"
echo " req 仅停止需求服务"
echo " exp 仅停止支出服务"
echo " receipt 仅停止票据服务"
echo " file 仅停止文件服务"
echo " report 仅停止报表服务"
echo " frontend 仅停止前端服务"
echo " help 显示此帮助信息"
echo ""
echo "示例:"
echo " $0 # 停止所有服务"
echo " $0 sys # 仅停止系统服务"
echo " $0 frontend # 仅停止前端"
}
# 主入口
case "$1" in
gateway)
stop_gateway
;;
sys)
stop_sys
;;
cust)
stop_cust
;;
proj)
stop_proj
;;
req)
stop_req
;;
exp)
stop_exp
;;
receipt)
stop_receipt
;;
file)
stop_file
;;
report)
stop_report
;;
frontend)
stop_frontend
;;
help|--help|-h)
show_help
;;
all|"")
stop_all
;;
*)
log_error "未知选项: $1"
show_help
exit 1
;;
esac