From a80591df77f4248dced90028f33564a92dfc1203 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 11 Feb 2026 11:22:32 +0800 Subject: [PATCH 1/2] add user page --- .../example/controller/UserController.java | 39 ++++ .../com/codingapi/example/entity/User.java | 5 + .../example/service/UserService.java | 24 +++ frontend/apps/app-pc/src/api/user.ts | 13 ++ frontend/apps/app-pc/src/config/routers.tsx | 22 ++- frontend/apps/app-pc/src/pages/home.tsx | 28 +-- frontend/apps/app-pc/src/pages/user.tsx | 182 ++++++++++++++++++ frontend/packages/flow-design/src/index.ts | 3 +- 8 files changed, 295 insertions(+), 21 deletions(-) create mode 100644 flow-engine-example/src/main/java/com/codingapi/example/controller/UserController.java create mode 100644 flow-engine-example/src/main/java/com/codingapi/example/service/UserService.java create mode 100644 frontend/apps/app-pc/src/api/user.ts create mode 100644 frontend/apps/app-pc/src/pages/user.tsx diff --git a/flow-engine-example/src/main/java/com/codingapi/example/controller/UserController.java b/flow-engine-example/src/main/java/com/codingapi/example/controller/UserController.java new file mode 100644 index 00000000..45a9c986 --- /dev/null +++ b/flow-engine-example/src/main/java/com/codingapi/example/controller/UserController.java @@ -0,0 +1,39 @@ +package com.codingapi.example.controller; + +import com.codingapi.example.entity.User; +import com.codingapi.example.repository.UserRepository; +import com.codingapi.example.service.UserService; +import com.codingapi.springboot.framework.dto.request.IdRequest; +import com.codingapi.springboot.framework.dto.request.SearchRequest; +import com.codingapi.springboot.framework.dto.response.MultiResponse; +import com.codingapi.springboot.framework.dto.response.Response; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/user") +@AllArgsConstructor +public class UserController { + + private final UserService userService; + + private final UserRepository userRepository; + + @GetMapping("/list") + public MultiResponse list(SearchRequest request){ + return MultiResponse.of(userRepository.searchRequest(request)); + } + + @PostMapping("/save") + public Response save(@RequestBody User request){ + userService.save(request); + return Response.buildSuccess(); + } + + @PostMapping("/remove") + public Response remove(@RequestBody IdRequest request){ + userService.remove(request.getLongId()); + return Response.buildSuccess(); + } + +} diff --git a/flow-engine-example/src/main/java/com/codingapi/example/entity/User.java b/flow-engine-example/src/main/java/com/codingapi/example/entity/User.java index bce8977b..6f3377fb 100644 --- a/flow-engine-example/src/main/java/com/codingapi/example/entity/User.java +++ b/flow-engine-example/src/main/java/com/codingapi/example/entity/User.java @@ -39,6 +39,11 @@ public static User admin(PasswordEncoder passwordEncoder){ } + public void encodePassword(PasswordEncoder passwordEncoder){ + this.password = passwordEncoder.encode(password); + } + + public List getRoles() { return List.of(ADMIN_ROLE); } diff --git a/flow-engine-example/src/main/java/com/codingapi/example/service/UserService.java b/flow-engine-example/src/main/java/com/codingapi/example/service/UserService.java new file mode 100644 index 00000000..7c4aec3f --- /dev/null +++ b/flow-engine-example/src/main/java/com/codingapi/example/service/UserService.java @@ -0,0 +1,24 @@ +package com.codingapi.example.service; + +import com.codingapi.example.entity.User; +import com.codingapi.example.repository.UserRepository; +import lombok.AllArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +@AllArgsConstructor +public class UserService { + + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + + public void save(User user) { + user.encodePassword(passwordEncoder); + userRepository.save(user); + } + + public void remove(long id) { + userRepository.deleteById(id); + } +} diff --git a/frontend/apps/app-pc/src/api/user.ts b/frontend/apps/app-pc/src/api/user.ts new file mode 100644 index 00000000..77b25f79 --- /dev/null +++ b/frontend/apps/app-pc/src/api/user.ts @@ -0,0 +1,13 @@ +import { httpClient } from "." + +export const list = (request: any) => { + return httpClient.page('/api/user/list', request, {}, {}, []); +} + +export const remove = (id:string) => { + return httpClient.post('/api/user/remove',{id}); +} + +export const save = (body:any) => { + return httpClient.post('/api/user/save',body); +} \ No newline at end of file diff --git a/frontend/apps/app-pc/src/config/routers.tsx b/frontend/apps/app-pc/src/config/routers.tsx index 80d9e9ec..be2b66b5 100644 --- a/frontend/apps/app-pc/src/config/routers.tsx +++ b/frontend/apps/app-pc/src/config/routers.tsx @@ -1,26 +1,36 @@ -import { createHashRouter, type RouteObject } from "react-router"; +import {createHashRouter} from "react-router"; import LoginPage from "@/pages/login"; import HomePage from "@/pages/home"; import DesignPage from "@/pages/desgin"; import TodoPage from "@/pages/todo"; +import UserPage from "@/pages/user.tsx"; -const routers:RouteObject[] = [ +export const routers = [ { path:'/login', - element: + element:, + name:'登陆界面', + }, + { + path:'/user', + element:, + name: '用户界面' }, { path:'/design', - element: + element:, + name: '流程设计' }, { path:'/todo', - element: + element:, + name: '待办中心' }, { path:'/', - element: + element:, + name: '系统主页' } ] diff --git a/frontend/apps/app-pc/src/pages/home.tsx b/frontend/apps/app-pc/src/pages/home.tsx index f6377d34..f8fd3d2e 100644 --- a/frontend/apps/app-pc/src/pages/home.tsx +++ b/frontend/apps/app-pc/src/pages/home.tsx @@ -1,28 +1,28 @@ import React from "react"; import { useNavigate } from "react-router"; import { Button, Flex, Space } from "antd"; +import {routers} from "@/config/routers.tsx"; const HomePage: React.FC = () => { const navigate = useNavigate(); + const routerButtons = routers.filter(item=>item.path!=='/'); + return (
-

Home Page

+

Flow-Engine Home Page

- - - + {routerButtons.map((item)=>{ + return ( + + ) + })}
) diff --git a/frontend/apps/app-pc/src/pages/user.tsx b/frontend/apps/app-pc/src/pages/user.tsx new file mode 100644 index 00000000..23ecebc0 --- /dev/null +++ b/frontend/apps/app-pc/src/pages/user.tsx @@ -0,0 +1,182 @@ +import React from "react"; +import {type ActionType, Table, type TableProps} from "@flow-engine/flow-design"; +import {Button, Form, Input, message, Modal, Popconfirm, Space, Switch} from "antd"; +import {list, remove, save} from "@/api/user.ts"; + +const UserPage = () => { + const actionType = React.useRef(null); + const [visible, setVisible] = React.useState(false); + const [form] = Form.useForm(); + + const columns: TableProps['columns'] = [ + { + dataIndex: 'id', + title: '编号', + }, + { + dataIndex: 'name', + title: '姓名', + }, + { + dataIndex: 'account', + title: '账户', + }, + { + dataIndex: 'flowManager', + title: '角色', + render: (value, record) => { + return value ? '流程管理员' : '普通用户' + } + }, + { + dataIndex: 'flowOperatorId', + title: '流程委托人', + render: (value, record) => { + if (!value) { + return '无' + } + return value + } + }, + { + dataIndex: 'option', + title: '操作', + render: (value, record) => { + return ( + + { + form.setFieldsValue({ + ...record, + password: '' + }); + setVisible(true); + }}>编辑 + {record.account !== 'admin' && ( + { + remove(record.id).then(() => { + message.success("用户已删除"); + actionType.current?.reload(); + }) + }} + > + 删除 + + )} + + ) + } + } + ]; + + return ( +
+ { + return [ + + ] + }} + columns={columns} + request={(request) => { + return list(request); + }} + /> + + { + setVisible(false); + }} + onOk={() => { + form.submit(); + }} + > +
{ + save(values).then(() => { + message.success("用户已保存"); + actionType.current?.reload(); + setVisible(false); + }) + }} + > + + + + + + + + + + + + + + + + + + + + + + + +
+ + ) +} + +export default UserPage; \ No newline at end of file diff --git a/frontend/packages/flow-design/src/index.ts b/frontend/packages/flow-design/src/index.ts index 29105e11..9b7de096 100644 --- a/frontend/packages/flow-design/src/index.ts +++ b/frontend/packages/flow-design/src/index.ts @@ -1,2 +1,3 @@ export * from '@/pages/design-list'; -export * from '@/pages/design-panel'; \ No newline at end of file +export * from '@/pages/design-panel'; +export * from '@/components/table'; \ No newline at end of file From 1a520e3477db4a3ad4c0f28f90b95dae89311c03 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 11 Feb 2026 11:52:30 +0800 Subject: [PATCH 2/2] add record.ts --- .../infra/jpa/FlowRecordEntityRepository.java | 22 +++- .../controller/FlowRecordQueryController.java | 51 ++++++++ frontend/apps/app-pc/src/api/record.ts | 13 ++ frontend/apps/app-pc/src/config/routers.tsx | 2 +- frontend/apps/app-pc/src/pages/todo.tsx | 120 +++++++++++++++++- 5 files changed, 200 insertions(+), 8 deletions(-) create mode 100644 flow-engine-starter-query/src/main/java/com/codingapi/flow/query/controller/FlowRecordQueryController.java create mode 100644 frontend/apps/app-pc/src/api/record.ts diff --git a/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/jpa/FlowRecordEntityRepository.java b/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/jpa/FlowRecordEntityRepository.java index 40a329d2..f008bd7e 100644 --- a/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/jpa/FlowRecordEntityRepository.java +++ b/flow-engine-starter-infra/src/main/java/com/codingapi/flow/infra/jpa/FlowRecordEntityRepository.java @@ -2,10 +2,13 @@ import com.codingapi.flow.infra.entity.FlowRecordEntity; import com.codingapi.springboot.fast.jpa.repository.FastRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.Query; import java.util.List; -public interface FlowRecordEntityRepository extends FastRepository { +public interface FlowRecordEntityRepository extends FastRepository { FlowRecordEntity getFlowRecordEntityById(long id); @@ -14,11 +17,20 @@ public interface FlowRecordEntityRepository extends FastRepository findFlowRecordEntityByFromIdAndNodeIdAndRevoked(long fromId, String nodeId, boolean revoked); List findFlowRecordEntityByProcessIdAndRecordStateAndFlowStateAndHiddenAndRevoked(String processId, - int recordState, - int flowState, - boolean hidden, - boolean revoked); + int recordState, + int flowState, + boolean hidden, + boolean revoked); List findFlowRecordEntityByProcessIdAndFromIdGreaterThanEqual(String processId, long fromId); + + @Query("from FlowRecordEntity r where r.currentOperatorId = ?1 and (r.recordState = 0 and r.flowState = 0 and r.hidden=false and r.revoked = false)") + Page findTodoPage(long operatorId, PageRequest pageRequest); + + + @Query("from FlowRecordEntity r where r.currentOperatorId = ?1 and (r.recordState = 1 and r.hidden=false and r.revoked = false) ") + Page findDonePage(long operatorId, PageRequest pageRequest); + + } diff --git a/flow-engine-starter-query/src/main/java/com/codingapi/flow/query/controller/FlowRecordQueryController.java b/flow-engine-starter-query/src/main/java/com/codingapi/flow/query/controller/FlowRecordQueryController.java new file mode 100644 index 00000000..505c0614 --- /dev/null +++ b/flow-engine-starter-query/src/main/java/com/codingapi/flow/query/controller/FlowRecordQueryController.java @@ -0,0 +1,51 @@ +package com.codingapi.flow.query.controller; + +import com.codingapi.flow.infra.entity.FlowRecordEntity; +import com.codingapi.flow.infra.jpa.FlowRecordEntityRepository; +import com.codingapi.flow.operator.IFlowOperator; +import com.codingapi.springboot.framework.dto.request.SearchRequest; +import com.codingapi.springboot.framework.dto.response.MultiResponse; +import com.codingapi.springboot.framework.user.UserContext; +import lombok.AllArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/query/record") +@AllArgsConstructor +public class FlowRecordQueryController { + + private final FlowRecordEntityRepository flowRecordEntityRepository; + + /** + * 全部流程记录接口 + */ + @GetMapping("/list") + public MultiResponse list(SearchRequest request) { + return MultiResponse.of(flowRecordEntityRepository.searchRequest(request)); + } + + + /** + * 我的待办记录 + */ + @GetMapping("/todo") + public MultiResponse todo(SearchRequest request) { + IFlowOperator current = (IFlowOperator) UserContext.getInstance().current(); + PageRequest pageRequest = request.toPageRequest(FlowRecordEntity.class); + return MultiResponse.of(flowRecordEntityRepository.findTodoPage(current.getUserId(),pageRequest)); + } + + + /** + * 我的已办记录 + */ + @GetMapping("/done") + public MultiResponse done(SearchRequest request) { + IFlowOperator current = (IFlowOperator) UserContext.getInstance().current(); + PageRequest pageRequest = request.toPageRequest(FlowRecordEntity.class); + return MultiResponse.of(flowRecordEntityRepository.findDonePage(current.getUserId(),pageRequest)); + } +} diff --git a/frontend/apps/app-pc/src/api/record.ts b/frontend/apps/app-pc/src/api/record.ts new file mode 100644 index 00000000..220b2d37 --- /dev/null +++ b/frontend/apps/app-pc/src/api/record.ts @@ -0,0 +1,13 @@ +import { httpClient } from "." + +export const list = (request: any) => { + return httpClient.page('/api/query/record/list', request, {}, {}, []); +} + +export const todo = (request: any) => { + return httpClient.page('/api/query/record/todo', request, {}, {}, []); +} + +export const done = (request: any) => { + return httpClient.page('/api/query/record/done', request, {}, {}, []); +} \ No newline at end of file diff --git a/frontend/apps/app-pc/src/config/routers.tsx b/frontend/apps/app-pc/src/config/routers.tsx index be2b66b5..17b481ce 100644 --- a/frontend/apps/app-pc/src/config/routers.tsx +++ b/frontend/apps/app-pc/src/config/routers.tsx @@ -15,7 +15,7 @@ export const routers = [ { path:'/user', element:, - name: '用户界面' + name: '用户管理' }, { path:'/design', diff --git a/frontend/apps/app-pc/src/pages/todo.tsx b/frontend/apps/app-pc/src/pages/todo.tsx index 334fe119..393b028a 100644 --- a/frontend/apps/app-pc/src/pages/todo.tsx +++ b/frontend/apps/app-pc/src/pages/todo.tsx @@ -1,11 +1,127 @@ import React from "react"; - +import {list,todo,done} from "@/api/record.ts"; +import {type ActionType, type TableProps,Table} from "@flow-engine/flow-design"; +import {Button, Space, Tabs,type TabsProps } from "antd"; const TodoPage:React.FC = ()=>{ + const actionAll = React.useRef(null); + const actionTodo = React.useRef(null); + const actionDone = React.useRef(null); + + const columns: TableProps['columns'] = [ + { + dataIndex: 'id', + title: '编号', + }, + { + dataIndex: 'title', + title: '流程名称', + }, + { + dataIndex: 'readTime', + title: '读取状态', + }, + { + dataIndex: 'createTime', + title: '创建时间', + }, + { + dataIndex: 'currentOperatorId', + title: '审批人', + }, + { + dataIndex: 'recordState', + title: '状态', + }, + { + dataIndex: 'option', + title: '操作', + render: (value, record) => { + return ( + + { + + }}>办理 + + ) + } + } + ]; + + + const items:TabsProps['items'] = [ + { + key:'all', + label:'全部流程', + children:( +
{ + return list(request); + }} + /> + ) + }, + { + key:'todo', + label:'我的待办', + children:( +
{ + return todo(request); + }} + /> + ) + }, + { + key:'done', + label:'我的已办', + children:( +
{ + return done(request); + }} + /> + ) + } + ] + return (
- todo + { + if(currentKey==='all'){ + actionAll.current?.reload(); + } + if(currentKey==='done'){ + actionDone.current?.reload(); + } + if(currentKey==='todo'){ + actionTodo.current?.reload(); + } + }} + tabBarExtraContent={{ + right:( + + ) + }} + />
) }