Recently, WHEN I was studying the work order system, I found a very bad work order system. As we all know, the most troublesome part of work order system is the maintenance of process and template, and some operations are likely to be added in the process of work order processing, which are called hooks. According to the results of my current research, there is no work order system can achieve such a good.
This work order system on the process design, template design and so on do very good, and the control of permissions is very detailed, including API interface, menu, page button permissions, can be flexible control, very good.
Demo:fdevops.com: 8001 / # / dashboard
Github:github.com/lanyulei/fe…
If you feel good, give the author a star. Your star may be the motivation for the author to keep going.
Function is introduced
System management
- User management not only includes users, but also the management of roles, positions and departments, which is convenient for the extension of work order processing personnel.
- Menu management, menu, page buttons, and even API interface management.
- Login log, which records user login and logout logs.
Process center
- Process application, process classification management, convenient maintenance and visualization.
- To-do list, split into 4 categories, including: my to-do list, my created to-do list, my associated to-do list, and all to-do lists.
- To hand over work orders to someone else if you are currently working on something else.
- End work order, if a work order development application is not correct, the authority is enough, it can directly end work order.
- The diversity of work order processing personnel can not only be handled individually, but also can be departments, roles and variables.
- Handler variables are automatically obtained based on user data, such as founder, founder leader, HRBP, etc.
- Countersign, if it is multiple selectors, and the countersign function is checked, then it will be passed only after these responsible persons have been processed.
- Task management can bind tasks to any stage, which is equivalent to the hook operation in the process. The effect achieved is that when the work order is completed, the task will be executed and a lot of labor costs will be reduced.
- The flexibility of notification can be tied to each phase by task or to the process globally.
- Gateway, supporting exclusive gateway and parallel gateway, the exclusive gateway is judged by the condition, as long as one condition passes, can enter the next stage; Parallel gateways, that is, all phases must be processed before the next phase can proceed
- There will also be a number of extensions, including: checkmark, prompt, sub-process and so on.
And so on there’s a lot more to study.
Data structure design
For a complete workflow system, we need to have a process, the template, grouping, users, tasks, etc., and all these things can be flexible customization, because if not flexible customization, for common use this is not very convenient, so for a good workflow system, is a must to achieve flexibility.
So let’s go straight to the design of the data structure.
Process classification
type Classify struct {
base.Model
Name string `gorm:"column:name; type: varchar(128)" json:"name" form:"name"'// Category name Creator int' gorm:"column:creator; type: int(11)" json:"creator" form:"creator"'// creator} func (Classify) TableName() string {return "process_classify"
}
Copy the code
process
type Info struct {
base.Model
Name string `gorm:"column:name; type:varchar(128)" json:"name" form:"name"'// Process name Structure json.rawMessage' gorm:"column:structure; type:json" json:"structure" form:"structure"'// process structure Classify int' gorm:"column:classify; type:int(11)" json:"classify" form:"classify"'// category ID Tpls json.RawMessage' gorm:"column:tpls; type:json" json:"tpls" form:"tpls"'// template Task json.RawMessage' gorm:"column:task; type:json" json:"task" form:"task"'// Task ID, array, can execute multiple tasks, can be used as a notification task, each node will execute Creator int' gorm:"column:creator; type:int(11)" json:"creator" form:"creator"'// creator} func (Info) TableName() string {return "process_info"
}
Copy the code
template
type Info struct {
base.Model
Name string `gorm:"column:name; type: varchar(128)" json:"name" form:"name" binding:"required"'// Template name FormStructure json.RawMessage' gorm:"column:form_structure; type: json" json:"form_structure" form:"form_structure" binding:"required"'// Form structure Creator int' gorm:"column:creator; type: int(11)" json:"creator" form:"creator"'// Remarks:"column:remarks; type: longtext" json:"remarks" form:"remarks"'// Remarks} func (Info) TableName() string {return "tpl_info"
}
Copy the code
The repair order
type Info struct {
base.Model
Title string `gorm:"column:title; type:varchar(128)" json:"title" form:"title"Process int 'gorm:"column:process; type:int(11)" json:"process" form:"process"'// Process ID Classify int' gorm:"column:classify; type:int(11)" json:"classify" form:"classify"'// category ID IsEnd int' gorm:"column:is_end; type:int(11); default:0" json:"is_end" form:"is_end"State json.RawMessage 'gorm:"column:state; type:json" json:"state" form:"state"'// RelatedPerson json.RawMessage' gorm:"column:related_person; type:json" json:"related_person" form:"related_person"'// Creator int' gorm:"column:creator; type:int(11)" json:"creator" form:"creator"'// creator} func (Info) TableName() string {return "work_order_info"
}
Copy the code
Work order binding template
type TplData struct {
base.Model
WorkOrder int `gorm:"column:work_order; type: int(11)" json:"work_order" form:"work_order"'// workorder ID FormStructure json.RawMessage' gorm:"column:form_structure; type: json" json:"form_structure" form:"form_structure"'// FormData json.RawMessage' gorm:"column:form_data; type: json" json:"form_data" form:"form_data"'// Form data} func (TplData) TableName() string {return "work_order_tpl_data"
}
Copy the code
Work order transfer history
type CirculationHistory struct {
base.Model
Title string `gorm:"column:title; type: varchar(128)" json:"title" form:"title"WorkOrder int 'gorm:"column:work_order; type: int(11)" json:"work_order" form:"work_order"'// ID State string' gorm:"column:state; type: varchar(128)" json:"state" form:"state"'// Work order status Source string' gorm:"column:source; type: varchar(128)" json:"source" form:"source"'// Source node ID Target string' gorm:"column:target; type: varchar(128)" json:"target" form:"target"'// Object ID Circulation String' GORm:"column:circulation; type: varchar(128)" json:"circulation" form:"circulation"'// Processor string' GORm:"column:processor; type: varchar(45)" json:"processor" form:"processor"'// ProcessorId int' gorm:"column:processor_id; type: int(11)" json:"processor_id" form:"processor_id"'// Handler ID CostDuration string' gorm:"column:cost_duration; type: varchar(128)" json:"cost_duration" form:"cost_duration"'// Processing"column:remarks; type: longtext" json:"remarks" form:"remarks"'// note} func (CirculationHistory) TableName() string {return "work_order_circulation_history"
}
Copy the code
task
type Info struct {
base.Model
Name string `gorm:"column:name; type: varchar(256)" json:"name" form:"name"'// TaskType string' gorm:"column:task_type; type: varchar(45)" json:"task_type" form:"task_type"'// Task Type Content String' gorm:"column:content; type: longtext" json:"content" form:"content"'// Task content Creator int' gorm:"column:creator; type: int(11)" json:"creator" form:"creator"'// Remarks:"column:remarks; type: longtext" json:"remarks" form:"remarks"'// Remarks} func (Info) TableName() string {return "task_info"
}
Copy the code
Task Execution History
type History struct {
base.Model
Task int `gorm:"column:task; type: int(11)" json:"task" form:"task"'// Task ID Name string' gorm:"column:name; type: varchar(256)" json:"name" form:"name"'// TaskType int' gorm:"column:task_type; type: int(11)" json:"task_type" form:"task_type"'// Task type, Python, shell ExecutionTime String' gorm:"column:execution_time; type: varchar(128)" json:"execution_time" form:"execution_time"'// Execution time Result string' gorm:"column:result; type: longtext" json:"result" form:"result"'// Task return} func (History) TableName() string {return "task_history"
}
Copy the code
Install the deployment
Go >= 1.14vue >= 2.6 NPM >= 6.14Copy the code
Secondary development
The back-end
# 1. Get the code
git https://github.com/lanyulei/ferry.git
# 2. Enter the work path
cd ./ferry
Ferry/config / # 3. Modify the configuration Settings. The dev. Yml
vi ferry/config/settings.dev.yml
Note:Program startup parameters 2. Database information 3. Log path# 4. Initialize database
go run main.go init -c=config/settings.dev.yml
# 5. Start the program
go run main.go server -c=config/settings.dev.yml
Copy the code
The front end
# 1. Get the code
git https://github.com/lanyulei/ferry_web.git
# 2. Enter the work path
cd ./ferry_web
# 3. Install dependencies
npm install
# 4. Start the program
npm run dev
Copy the code
On-line deployment
The back-end
# 1. Enter the project path and cross-compile (centos)Env GOOS = Linux GOARCH = amd64 go build cross-compilation content more, please visit https://www.fdevops.com/2020/03/08/go-locale-configurationUpload the config directory to the project root and verify that the configuration information is correct
vi ferry/config/settings.yml
Note:Program startup parameters 2. Database information 3. Log path# 3. Create log path and static file experience
mkdir -p log static/uploadfile
# 4. Initialize data
./ferry init -c=config/settings.yml
# 5. Start the program. It is recommended to use the "Process Management Tool" for startup maintenance
nohup ./ferry server -c=config/settings.yml > /dev/null 2>&1 &
Copy the code
The front end
# 1. The compilation
npm run build:prod
# 2. Upload the dist directory to the project path.
mv dist web
# 3. Nginx configuration, according to the business can be adjusted
server {
listen 8001; # monitor port
server_name localhost; # Multiple domain names can be separated by Spaces
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /data/ferry/web;
index index.html index.htm; If there is no match for index.html, search for index.htm, and so on
}
# SSL configuration omitted
location /api {
# rewrite ^.+api/? (.*)$ /$1 break;Proxy_pass http://127.0.0.1:8002;Node API Server requires the IP address of the proxy
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# landingLocation /login {proxy_pass http://127.0.0.1:8002;Node API Server requires the IP address of the proxy
}
# the refresh tokenLocation /refresh_token {proxy_pass http://127.0.0.1:8002;Node API Server requires the IP address of the proxy
}
Interface addressLocation /swagger {proxy_pass http://127.0.0.1:8002;Node API Server requires the IP address of the proxy
}
Backend static file pathLocation /static/uploadfile {proxy_pass http://127.0.0.1:8002;Node API Server requires the IP address of the proxy
}
#error_page 404 /404.html; Error page 404.html is configured
# redirect server error pages to the static page /50x.html
Redirect the server error page to static page /50x.html
#error_page 500 502 503 504 /50x.html; location = /50x.html { root html; }}Copy the code