@specpow/framework 0.5.21 → 0.5.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/builtin/schemas/mes-crud-module/schema.yaml +158 -0
- package/builtin/schemas/mes-crud-module/templates/api-docs.md +238 -0
- package/builtin/schemas/mes-crud-module/templates/design.md +422 -0
- package/builtin/schemas/mes-crud-module/templates/menu-register.md +126 -0
- package/builtin/schemas/mes-crud-module/templates/proposal.md +64 -0
- package/builtin/schemas/mes-crud-module/templates/spec.md +94 -0
- package/builtin/schemas/mes-crud-module/templates/tasks.md +208 -0
- package/builtin/schemas/mes-prd-to-code/schema.yaml +365 -0
- package/builtin/schemas/mes-prd-to-code/templates/code-generation.md +81 -0
- package/builtin/schemas/mes-prd-to-code/templates/confirm-scope.md +51 -0
- package/builtin/schemas/mes-prd-to-code/templates/parse.md +168 -0
- package/builtin/schemas/mes-prd-to-code/templates/tasks.md +162 -0
- package/builtin/schemas/mes-prd-to-code/templates/tech-design.md +310 -0
- package/builtin/schemas/prd-to-tech-doc/schema.yaml +161 -0
- package/builtin/schemas/prd-to-tech-doc/templates/analysis.md +133 -0
- package/builtin/schemas/prd-to-tech-doc/templates/tech-design.md +263 -0
- package/builtin/schemas/prd-to-tech-doc/templates/tech-tasks.md +134 -0
- package/package.json +1 -1
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
# MES CRUD 模块设计: {{Entity}}
|
|
2
|
+
|
|
3
|
+
> 模块: {{module}} | 业务中心: {{module}}-center | 表: {{table_name}}
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. 数据库设计(Oracle)
|
|
8
|
+
|
|
9
|
+
### 建表 SQL
|
|
10
|
+
|
|
11
|
+
```sql
|
|
12
|
+
CREATE TABLE {{table_name}} (
|
|
13
|
+
id NUMBER(19) NOT NULL,
|
|
14
|
+
-- ============================================================
|
|
15
|
+
-- 业务字段(根据提案中的字段清单补充)
|
|
16
|
+
-- ============================================================
|
|
17
|
+
is_effect NUMBER(1) DEFAULT 1,
|
|
18
|
+
-- ============================================================
|
|
19
|
+
-- 审计字段(BaseModel / TimeModel 自动填充)
|
|
20
|
+
-- ============================================================
|
|
21
|
+
created_by VARCHAR2(64) DEFAULT '',
|
|
22
|
+
created_date TIMESTAMP DEFAULT SYSTIMESTAMP,
|
|
23
|
+
last_updated_by VARCHAR2(64) DEFAULT '',
|
|
24
|
+
last_updated_date TIMESTAMP DEFAULT SYSTIMESTAMP,
|
|
25
|
+
-- ============================================================
|
|
26
|
+
CONSTRAINT pk_{{table_name}} PRIMARY KEY (id)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
COMMENT ON TABLE {{table_name}} IS '{{table_comment}}';
|
|
30
|
+
-- COMMENT ON COLUMN {{table_name}}.xxx IS 'xxx';
|
|
31
|
+
|
|
32
|
+
-- 雪花算法 ID 序列(可选,若由应用层生成则不需要)
|
|
33
|
+
-- CREATE SEQUENCE seq_{{table_name}} START WITH 1 INCREMENT BY 1 NOCACHE;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 2. 后端代码结构
|
|
39
|
+
|
|
40
|
+
### 目录布局
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
modules-center/{{module}}-center/{{module}}-service/
|
|
44
|
+
└── src/main/java/com/twsz/mom/{{module}}/
|
|
45
|
+
├── controller/
|
|
46
|
+
│ └── {{Entity}}Controller.java
|
|
47
|
+
├── service/
|
|
48
|
+
│ ├── {{Entity}}Service.java
|
|
49
|
+
│ └── impl/
|
|
50
|
+
│ └── {{Entity}}ServiceImpl.java
|
|
51
|
+
├── mapper/
|
|
52
|
+
│ └── {{Entity}}Mapper.java
|
|
53
|
+
└── model/
|
|
54
|
+
└── {{Entity}}.java
|
|
55
|
+
|
|
56
|
+
└── src/main/resources/
|
|
57
|
+
└── mapper/
|
|
58
|
+
└── {{Entity}}Mapper.xml
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Entity(继承 BaseModel)
|
|
62
|
+
|
|
63
|
+
```java
|
|
64
|
+
@Data
|
|
65
|
+
@EqualsAndHashCode(callSuper = true)
|
|
66
|
+
@JsonInclude(JsonInclude.Include.NON_NULL)
|
|
67
|
+
@TableName("{{table_name}}")
|
|
68
|
+
public class {{Entity}} extends BaseModel {
|
|
69
|
+
// 业务字段(审计字段由 BaseModel → TimeModel 提供)
|
|
70
|
+
private Integer isEffect;
|
|
71
|
+
// ... 根据提案补充
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
> **BaseModel 继承链**: `ViewModel`(fields/columns/orderBy/动态查询)→ `TimeModel`(审计字段自动填充)→ `BaseModel`(id 雪花算法)
|
|
76
|
+
|
|
77
|
+
### Mapper(继承 BaseMapper)
|
|
78
|
+
|
|
79
|
+
```java
|
|
80
|
+
@Mapper
|
|
81
|
+
public interface {{Entity}}Mapper extends BaseMapper<{{Entity}}> {
|
|
82
|
+
IPage<{{Entity}}> pageSearch(Page<{{Entity}}> page, @Param("entity") {{Entity}} entity);
|
|
83
|
+
List<{{Entity}}> list(@Param("entity") {{Entity}} entity);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Mapper XML(标准片段模板)
|
|
88
|
+
|
|
89
|
+
```xml
|
|
90
|
+
<mapper namespace="com.twsz.mom.{{module}}.mapper.{{Entity}}Mapper">
|
|
91
|
+
<resultMap id="BaseResultMap" type="com.twsz.mom.{{module}}.model.{{Entity}}"/>
|
|
92
|
+
|
|
93
|
+
<!-- 动态列(支持 ViewModel.fields 部分列查询) -->
|
|
94
|
+
<sql id="{{Entity}}Columns">
|
|
95
|
+
<choose>
|
|
96
|
+
<when test="entity.fields != null">
|
|
97
|
+
<foreach collection="entity.columns" item="it" separator=",">t.${it}</foreach>
|
|
98
|
+
</when>
|
|
99
|
+
<otherwise>t.*</otherwise>
|
|
100
|
+
</choose>
|
|
101
|
+
</sql>
|
|
102
|
+
|
|
103
|
+
<!-- 动态 WHERE(每个字段一个 if 判断) -->
|
|
104
|
+
<sql id="{{Entity}}Where">
|
|
105
|
+
<where>
|
|
106
|
+
<if test="entity.id != null">AND t.id = #{entity.id}</if>
|
|
107
|
+
<if test="entity.isEffect != null">AND t.is_effect = #{entity.isEffect}</if>
|
|
108
|
+
<!-- 根据字段清单补充 -->
|
|
109
|
+
</where>
|
|
110
|
+
<choose>
|
|
111
|
+
<when test="entity.orderBy != null and entity.orderBy != ''">
|
|
112
|
+
ORDER BY t.${entity.orderBy}
|
|
113
|
+
</when>
|
|
114
|
+
<otherwise>ORDER BY t.id DESC</otherwise>
|
|
115
|
+
</choose>
|
|
116
|
+
</sql>
|
|
117
|
+
|
|
118
|
+
<sql id="{{Entity}}Joins"></sql>
|
|
119
|
+
|
|
120
|
+
<select id="pageSearch" resultMap="BaseResultMap">
|
|
121
|
+
SELECT <include refid="{{Entity}}Columns"/>
|
|
122
|
+
FROM {{table_name}} t
|
|
123
|
+
<include refid="{{Entity}}Joins"/>
|
|
124
|
+
<include refid="{{Entity}}Where"/>
|
|
125
|
+
</select>
|
|
126
|
+
|
|
127
|
+
<select id="list" resultMap="BaseResultMap">
|
|
128
|
+
SELECT * FROM (
|
|
129
|
+
SELECT <include refid="{{Entity}}Columns"/>
|
|
130
|
+
FROM {{table_name}} t
|
|
131
|
+
<include refid="{{Entity}}Joins"/>
|
|
132
|
+
<include refid="{{Entity}}Where"/>
|
|
133
|
+
) tt WHERE rownum <=
|
|
134
|
+
<choose>
|
|
135
|
+
<when test="entity.rowNum != null">#{entity.rowNum}</when>
|
|
136
|
+
<otherwise>10000</otherwise>
|
|
137
|
+
</choose>
|
|
138
|
+
</select>
|
|
139
|
+
</mapper>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Service 接口
|
|
143
|
+
|
|
144
|
+
```java
|
|
145
|
+
public interface {{Entity}}Service extends IService<{{Entity}}> {
|
|
146
|
+
ResponseWrapper<String> insert({{Entity}} entity);
|
|
147
|
+
ResponseWrapper<String> update({{Entity}} entity);
|
|
148
|
+
ResponseWrapper<String> deleteByIds(Collection<Long> ids);
|
|
149
|
+
ResponseWrapper<Page<{{Entity}}>> search(PageForm<{{Entity}}> pageForm);
|
|
150
|
+
List<{{Entity}}> list({{Entity}} entity);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Service 实现
|
|
155
|
+
|
|
156
|
+
```java
|
|
157
|
+
@Slf4j
|
|
158
|
+
@Service
|
|
159
|
+
public class {{Entity}}ServiceImpl extends ServiceImpl<{{Entity}}Mapper, {{Entity}}>
|
|
160
|
+
implements {{Entity}}Service {
|
|
161
|
+
|
|
162
|
+
@Override
|
|
163
|
+
public ResponseWrapper<String> insert({{Entity}} entity) {
|
|
164
|
+
if (super.save(entity)) {
|
|
165
|
+
return ResponseWrapper.defaultSuccess();
|
|
166
|
+
}
|
|
167
|
+
return ResponseWrapper.ofStatus(HttpStatus.OBJECT_INSERT_FAIL);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@Override
|
|
171
|
+
public ResponseWrapper<String> update({{Entity}} entity) {
|
|
172
|
+
if (super.updateById(entity)) {
|
|
173
|
+
return ResponseWrapper.defaultSuccess();
|
|
174
|
+
}
|
|
175
|
+
return ResponseWrapper.ofStatus(HttpStatus.OBJECT_UPDATE_FAIL);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@Override
|
|
179
|
+
public ResponseWrapper<String> deleteByIds(Collection<Long> ids) {
|
|
180
|
+
if (super.removeByIds(ids)) {
|
|
181
|
+
return ResponseWrapper.defaultSuccess();
|
|
182
|
+
}
|
|
183
|
+
return ResponseWrapper.ofStatus(HttpStatus.OBJECT_DELETE_FAIL);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@Override
|
|
187
|
+
public ResponseWrapper<Page<{{Entity}}>> search(PageForm<{{Entity}}> pageForm) {
|
|
188
|
+
Page<{{Entity}}> p = new Page<>(pageForm.getCurrent(), pageForm.getSize());
|
|
189
|
+
baseMapper.pageSearch(p, pageForm.getCondition());
|
|
190
|
+
return ResponseWrapper.ofSuccess(p);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@Override
|
|
194
|
+
public List<{{Entity}}> list({{Entity}} entity) {
|
|
195
|
+
return baseMapper.list(entity);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Controller
|
|
201
|
+
|
|
202
|
+
```java
|
|
203
|
+
@Slf4j
|
|
204
|
+
@RestController
|
|
205
|
+
@RequestMapping("/{{entity}}")
|
|
206
|
+
public class {{Entity}}Controller {
|
|
207
|
+
|
|
208
|
+
@Resource
|
|
209
|
+
private {{Entity}}Service {{entity}}Service;
|
|
210
|
+
|
|
211
|
+
@PostMapping(value = "add")
|
|
212
|
+
public ResponseWrapper<String> add(@RequestBody {{Entity}} entity) {
|
|
213
|
+
return {{entity}}Service.insert(entity);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@PutMapping(value = "update")
|
|
217
|
+
public ResponseWrapper<String> update(@RequestBody {{Entity}} entity) {
|
|
218
|
+
return {{entity}}Service.update(entity);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
@PostMapping(value = "search")
|
|
222
|
+
public ResponseWrapper<Page<{{Entity}}>> search(@RequestBody PageForm<{{Entity}}> pageForm) {
|
|
223
|
+
return {{entity}}Service.search(pageForm);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
@DeleteMapping(value = "delete")
|
|
227
|
+
public ResponseWrapper<String> delete(@RequestBody Long[] ids) {
|
|
228
|
+
return {{entity}}Service.deleteByIds(Arrays.asList(ids));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
@GetMapping(value = "get/{id}")
|
|
232
|
+
public ResponseWrapper<{{Entity}}> get(@PathVariable Long id) {
|
|
233
|
+
return ResponseWrapper.ofSuccess({{entity}}Service.getById(id));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
@PostMapping(value = "list")
|
|
237
|
+
public ResponseWrapper<List<{{Entity}}>> list(@RequestBody {{Entity}} entity) {
|
|
238
|
+
return ResponseWrapper.ofSuccess({{entity}}Service.list(entity));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@PostMapping(value = "export")
|
|
242
|
+
public void export(@RequestBody {{Entity}} condition, HttpServletResponse response) throws IOException {
|
|
243
|
+
// EasyExcel 导出
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 3. 前端代码结构
|
|
251
|
+
|
|
252
|
+
### 目录布局
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
src/
|
|
256
|
+
├── api/{{domain}}/
|
|
257
|
+
│ └── {{entity}}.js
|
|
258
|
+
└── views/{{domain}}/{{entity}}/
|
|
259
|
+
├── index.vue # 列表页
|
|
260
|
+
└── {{entity}}-form.vue # 表单页
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### API 接口文件
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
import axios from '@/libs/request'
|
|
267
|
+
import { exportExcel } from '../file'
|
|
268
|
+
|
|
269
|
+
const apiPrefix = '/{{entity}}'
|
|
270
|
+
|
|
271
|
+
const search{{Entity}} = data => {
|
|
272
|
+
return axios.request({ url: `${apiPrefix}/search`, method: 'POST', data })
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const save{{Entity}} = data => {
|
|
276
|
+
const url = data.id ? `${apiPrefix}/update` : `${apiPrefix}/add`
|
|
277
|
+
const method = data.id ? 'PUT' : 'POST'
|
|
278
|
+
return axios.request({ url, method, data })
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const delete{{Entity}} = ids => {
|
|
282
|
+
return axios.request({
|
|
283
|
+
url: `${apiPrefix}/delete`,
|
|
284
|
+
method: 'DELETE',
|
|
285
|
+
data: Array.isArray(ids) ? ids : [ids]
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const list{{Entity}} = data => {
|
|
290
|
+
return axios.request({ url: `${apiPrefix}/list`, method: 'POST', data })
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const export{{Entity}} = data => {
|
|
294
|
+
return exportExcel(`${apiPrefix}/export`, data)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export default { search{{Entity}}, save{{Entity}}, delete{{Entity}}, list{{Entity}}, export{{Entity}} }
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### 列表页(index.vue)
|
|
301
|
+
|
|
302
|
+
```vue
|
|
303
|
+
<template>
|
|
304
|
+
<div class="master-index">
|
|
305
|
+
<search-table v-show="showIndexPage" ref="searchTable" :option="option" :columns="tableColumns">
|
|
306
|
+
<template #condition-form>
|
|
307
|
+
<Form ref="conditionForm" class="search-condition-form" label-colon @keyup.enter.native="refresh">
|
|
308
|
+
<!-- 搜索条件表单字段 -->
|
|
309
|
+
</Form>
|
|
310
|
+
</template>
|
|
311
|
+
</search-table>
|
|
312
|
+
<tw-card v-show="!showIndexPage" :back="triggerBack">
|
|
313
|
+
<{{Entity}}Form :readonly="readonly" :form="formData" :key="instanceKey"
|
|
314
|
+
@on-success="handleSuccess" />
|
|
315
|
+
</tw-card>
|
|
316
|
+
</div>
|
|
317
|
+
</template>
|
|
318
|
+
|
|
319
|
+
<script>
|
|
320
|
+
import { indexPage } from '_c/table-form/index-mixin'
|
|
321
|
+
import {{Entity}}Api from '@/api/{{domain}}/{{entity}}'
|
|
322
|
+
import {{Entity}}Form from './{{entity}}-form'
|
|
323
|
+
|
|
324
|
+
export default {
|
|
325
|
+
mixins: [indexPage],
|
|
326
|
+
components: { {{Entity}}Form },
|
|
327
|
+
data() {
|
|
328
|
+
return {
|
|
329
|
+
option: {
|
|
330
|
+
tableName: '{{entity}}',
|
|
331
|
+
searchForm: {},
|
|
332
|
+
fixedTableHeight: true,
|
|
333
|
+
add: { enable: true, method: this.add },
|
|
334
|
+
edit: { enable: true, method: this.edit },
|
|
335
|
+
view: { enable: true, method: this.view },
|
|
336
|
+
delete: { enable: true, method: {{Entity}}Api.delete{{Entity}} },
|
|
337
|
+
export: { enable: true, method: {{Entity}}Api.export{{Entity}} },
|
|
338
|
+
search: { query: {{Entity}}Api.search{{Entity}} },
|
|
339
|
+
},
|
|
340
|
+
tableColumns: [
|
|
341
|
+
{ type: 'selection' },
|
|
342
|
+
// 表格列定义
|
|
343
|
+
{ title: this.$t('operate||操作'), slot: 'operate' }
|
|
344
|
+
]
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
</script>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### 表单页({{entity}}-form.vue)
|
|
352
|
+
|
|
353
|
+
```vue
|
|
354
|
+
<template>
|
|
355
|
+
<master-sub :readonly="readonly">
|
|
356
|
+
<template v-if="!readonly" #master-header-toolbar>
|
|
357
|
+
<Button type="primary" :loading="loading" @click="doSubmit">{{ $t('submit||提交') }}</Button>
|
|
358
|
+
</template>
|
|
359
|
+
<template #master-form>
|
|
360
|
+
<Form ref="mform" :model="mform" :label-width="120" :disabled="readonly" class="form">
|
|
361
|
+
<!-- 表单字段 -->
|
|
362
|
+
</Form>
|
|
363
|
+
</template>
|
|
364
|
+
</master-sub>
|
|
365
|
+
</template>
|
|
366
|
+
|
|
367
|
+
<script>
|
|
368
|
+
import { BaseMixin } from '@/mixin'
|
|
369
|
+
import {{Entity}}Api from '@/api/{{domain}}/{{entity}}'
|
|
370
|
+
|
|
371
|
+
export default {
|
|
372
|
+
mixins: [BaseMixin],
|
|
373
|
+
props: {
|
|
374
|
+
readonly: { type: Boolean, default: false },
|
|
375
|
+
form: { type: Object, default: () => ({}) }
|
|
376
|
+
},
|
|
377
|
+
data() { return { mform: {}, default: {} } },
|
|
378
|
+
created() {
|
|
379
|
+
this.mform = Object.assign({}, this.default, this.form)
|
|
380
|
+
},
|
|
381
|
+
methods: {
|
|
382
|
+
doSubmit() {
|
|
383
|
+
this.$refs.mform.validate(valid => {
|
|
384
|
+
if (valid) {
|
|
385
|
+
this.asyncLoading({{Entity}}Api.save{{Entity}}(this.mform)).then(res => {
|
|
386
|
+
this.$Message.success({ background: true, content: this.$t('submit.success||提交成功') })
|
|
387
|
+
this.$emit('on-success', this.mform)
|
|
388
|
+
})
|
|
389
|
+
}
|
|
390
|
+
})
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
</script>
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## 4. 接口定义
|
|
400
|
+
|
|
401
|
+
| Method | Path | 描述 | 请求体 | 响应 |
|
|
402
|
+
|--------|------|------|--------|------|
|
|
403
|
+
| POST | /{{entity}}/search | 分页查询 | `PageForm<{{Entity}}>` | `ResponseWrapper<Page<{{Entity}}>>` |
|
|
404
|
+
| POST | /{{entity}}/add | 新增 | `{{Entity}}` | `ResponseWrapper<String>` |
|
|
405
|
+
| PUT | /{{entity}}/update | 修改 | `{{Entity}}` | `ResponseWrapper<String>` |
|
|
406
|
+
| DELETE | /{{entity}}/delete | 批量删除 | `Long[]` | `ResponseWrapper<String>` |
|
|
407
|
+
| GET | /{{entity}}/get/{id} | 详情 | — | `ResponseWrapper<{{Entity}}>` |
|
|
408
|
+
| POST | /{{entity}}/list | 不分页列表 | `{{Entity}}` | `ResponseWrapper<List<{{Entity}}>>` |
|
|
409
|
+
| POST | /{{entity}}/export | Excel 导出 | `{{Entity}}` | Excel 文件流 |
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## 5. 前后端分工
|
|
414
|
+
|
|
415
|
+
| 功能 | 前端 | 后端 |
|
|
416
|
+
|------|------|------|
|
|
417
|
+
| 列表页 | search-table + indexPage mixin | POST /search(PageForm 分页) |
|
|
418
|
+
| 新增 | master-sub 表单 + save(无 id → add) | POST /add |
|
|
419
|
+
| 编辑 | master-sub 表单 + save(有 id → update) | PUT /update |
|
|
420
|
+
| 删除 | 确认弹窗 + delete(ids) | DELETE /delete |
|
|
421
|
+
| 导出 | exportExcel Blob 下载 | POST /export(EasyExcel) |
|
|
422
|
+
| 菜单 | 动态路由(后端返回 component 路径) | c_sys_resource 注册 |
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# 菜单注册: {{Entity}}
|
|
2
|
+
|
|
3
|
+
> 模块: {{module}} | 实体: {{Entity}} | 组件路径: `{{domain}}/{{entity}}/index`
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. 菜单配置
|
|
8
|
+
|
|
9
|
+
| 配置项 | 值 | 说明 |
|
|
10
|
+
|--------|-----|------|
|
|
11
|
+
| 菜单名称 | {{moduleName}} | 显示在侧边栏的名称 |
|
|
12
|
+
| 父级菜单 | 根据实际选择 | c_sys_resource.pid |
|
|
13
|
+
| 排序 | 100 | 同级菜单排序 |
|
|
14
|
+
| 图标 | ios-folder | ViewUI 图标名称 |
|
|
15
|
+
| 路由路径 | /{{domain}}/{{entity}} | 前端路由 |
|
|
16
|
+
| 组件路径 | {{domain}}/{{entity}}/index | Vue 组件路径(相对 src/views/) |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. c_sys_resource 表结构
|
|
21
|
+
|
|
22
|
+
| 字段 | 类型 | 说明 |
|
|
23
|
+
|------|------|------|
|
|
24
|
+
| id | bigint | 主键(雪花算法) |
|
|
25
|
+
| title | varchar | 菜单/按钮显示名称 |
|
|
26
|
+
| pid | bigint | 父级 ID(0 = 顶级) |
|
|
27
|
+
| sort | int | 排序号 |
|
|
28
|
+
| enable | int | 1=启用 |
|
|
29
|
+
| type | int | 1=路由菜单, 2=按钮权限, 3=API 资源 |
|
|
30
|
+
| path | varchar | URL 路径 |
|
|
31
|
+
| component | varchar | Vue 组件路径 |
|
|
32
|
+
| icon | varchar | 图标 |
|
|
33
|
+
| permission | varchar | 权限标识 |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 3. SQL 插入语句
|
|
38
|
+
|
|
39
|
+
```sql
|
|
40
|
+
-- ============================================================
|
|
41
|
+
-- 菜单注册(c_sys_resource)
|
|
42
|
+
-- ============================================================
|
|
43
|
+
|
|
44
|
+
-- 1. 菜单项(type=1 路由菜单)
|
|
45
|
+
-- 请先确认父级菜单 ID(替换下面的 @parentId)
|
|
46
|
+
-- 可通过 SELECT id FROM c_sys_resource WHERE title = '父级菜单名' 获取
|
|
47
|
+
|
|
48
|
+
INSERT INTO c_sys_resource (id, title, pid, sort, enable, type, path, component, icon, permission, created_by, created_date, last_updated_by, last_updated_date)
|
|
49
|
+
VALUES (
|
|
50
|
+
-- id: 使用雪花算法生成,或手动指定一个不冲突的大数字
|
|
51
|
+
{{menu_id}}, -- 菜单 ID(需唯一)
|
|
52
|
+
'{{moduleName}}', -- 菜单名称
|
|
53
|
+
{{parent_id}}, -- 父级菜单 ID(0=顶级)
|
|
54
|
+
100, -- 排序号
|
|
55
|
+
1, -- 启用
|
|
56
|
+
1, -- type=1 路由菜单
|
|
57
|
+
'/{{domain}}/{{entity}}', -- 路由路径
|
|
58
|
+
'{{domain}}/{{entity}}/index', -- 组件路径
|
|
59
|
+
'ios-folder', -- 图标
|
|
60
|
+
'{{module}}:{{entity}}:view', -- 权限标识
|
|
61
|
+
'admin',
|
|
62
|
+
SYSTIMESTAMP,
|
|
63
|
+
'admin',
|
|
64
|
+
SYSTIMESTAMP
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
-- 2. 按钮权限(type=2 按钮)
|
|
68
|
+
-- @menuId 替换为上面插入的菜单 ID
|
|
69
|
+
|
|
70
|
+
INSERT INTO c_sys_resource (id, title, pid, sort, enable, type, path, component, icon, permission, created_by, created_date, last_updated_by, last_updated_date)
|
|
71
|
+
VALUES
|
|
72
|
+
-- 查询按钮
|
|
73
|
+
({{menu_id}} + 1, '{{moduleName}}查询', {{menu_id}}, 1, 1, 2, '#', '', '', '{{module}}:{{entity}}:view', 'admin', SYSTIMESTAMP, 'admin', SYSTIMESTAMP),
|
|
74
|
+
-- 新增按钮
|
|
75
|
+
({{menu_id}} + 2, '{{moduleName}}新增', {{menu_id}}, 2, 1, 2, '#', '', '', '{{module}}:{{entity}}:add', 'admin', SYSTIMESTAMP, 'admin', SYSTIMESTAMP),
|
|
76
|
+
-- 修改按钮
|
|
77
|
+
({{menu_id}} + 3, '{{moduleName}}修改', {{menu_id}}, 3, 1, 2, '#', '', '', '{{module}}:{{entity}}:edit', 'admin', SYSTIMESTAMP, 'admin', SYSTIMESTAMP),
|
|
78
|
+
-- 删除按钮
|
|
79
|
+
({{menu_id}} + 4, '{{moduleName}}删除', {{menu_id}}, 4, 1, 2, '#', '', '', '{{module}}:{{entity}}:delete', 'admin', SYSTIMESTAMP, 'admin', SYSTIMESTAMP),
|
|
80
|
+
-- 导出按钮
|
|
81
|
+
({{menu_id}} + 5, '{{moduleName}}导出', {{menu_id}}, 5, 1, 2, '#', '', '', '{{module}}:{{entity}}:export', 'admin', SYSTIMESTAMP, 'admin', SYSTIMESTAMP);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 4. 权限标识
|
|
87
|
+
|
|
88
|
+
| 权限标识 | 描述 | 前端使用方式 |
|
|
89
|
+
|---------|------|-------------|
|
|
90
|
+
| `{{module}}:{{entity}}:view` | 查看 | `v-hasPerm="'{{module}}:{{entity}}:view'"` |
|
|
91
|
+
| `{{module}}:{{entity}}:add` | 新增 | `v-hasPerm="'{{module}}:{{entity}}:add'"` |
|
|
92
|
+
| `{{module}}:{{entity}}:edit` | 修改 | `v-hasPerm="'{{module}}:{{entity}}:edit'"` |
|
|
93
|
+
| `{{module}}:{{entity}}:delete` | 删除 | `v-hasPerm="'{{module}}:{{entity}}:delete'"` |
|
|
94
|
+
| `{{module}}:{{entity}}:export` | 导出 | `v-hasPerm="'{{module}}:{{entity}}:export'"` |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 5. 前端路由(参考)
|
|
99
|
+
|
|
100
|
+
> MES 项目使用动态路由:后端通过 `c_sys_resource` 返回路由配置,前端通过 `router.addRoutes()` 注册。
|
|
101
|
+
> 以下仅为参考,实际路由由后端 `GET /resource/listTreeByUser` 接口返回。
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
// 后端返回的路由数据结构(resources)
|
|
105
|
+
{
|
|
106
|
+
title: '{{moduleName}}',
|
|
107
|
+
path: '/{{domain}}/{{entity}}',
|
|
108
|
+
component: '{{domain}}/{{entity}}/index', // 前端 import.meta.glob 解析
|
|
109
|
+
icon: 'ios-folder',
|
|
110
|
+
permission: '{{module}}:{{entity}}:view',
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 6. 刷新缓存
|
|
117
|
+
|
|
118
|
+
执行 SQL 后,需要刷新 Redis 中的资源缓存:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# 通过 API 刷新
|
|
122
|
+
curl -X GET http://localhost:8000/api/sys/resource/reloadCache \
|
|
123
|
+
-H "Authorization: Bearer <token>"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
> **注意**: 执行 SQL 前请先确认 `pid` 的值(根据实际父级菜单调整),菜单 ID 需确保全局唯一。
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# MES CRUD 模块提案
|
|
2
|
+
|
|
3
|
+
## 模块信息
|
|
4
|
+
|
|
5
|
+
| 配置项 | 值 |
|
|
6
|
+
|--------|-----|
|
|
7
|
+
| 模块名称 | {{moduleName}} |
|
|
8
|
+
| 所属业务中心 | modules-center/{{module}}-center |
|
|
9
|
+
| 后端包名 | com.twsz.mom.{{module}} |
|
|
10
|
+
| 前端域 | src/views/{{domain}}/ |
|
|
11
|
+
|
|
12
|
+
## 核心实体
|
|
13
|
+
|
|
14
|
+
| 配置项 | 值 |
|
|
15
|
+
|--------|-----|
|
|
16
|
+
| 实体类名 | {{Entity}} |
|
|
17
|
+
| 表名 | {{table_name}} |
|
|
18
|
+
| 表注释 | {{table_comment}} |
|
|
19
|
+
| 描述 | {{description}} |
|
|
20
|
+
|
|
21
|
+
## CRUD 操作
|
|
22
|
+
|
|
23
|
+
- [ ] 分页查询(POST /{{entity}}/search)
|
|
24
|
+
- [ ] 详情查询(GET /{{entity}}/get/{id})
|
|
25
|
+
- [ ] 新增(POST /{{entity}}/add)
|
|
26
|
+
- [ ] 修改(PUT /{{entity}}/update)
|
|
27
|
+
- [ ] 批量删除(DELETE /{{entity}}/delete)
|
|
28
|
+
- [ ] 不分页列表(POST /{{entity}}/list)
|
|
29
|
+
- [ ] Excel 导出(POST /{{entity}}/export)
|
|
30
|
+
|
|
31
|
+
## 字段清单
|
|
32
|
+
|
|
33
|
+
| 字段名 | 数据库列名 | Oracle 类型 | Java 类型 | 必填 | 说明 |
|
|
34
|
+
|--------|-----------|-------------|-----------|------|------|
|
|
35
|
+
| id | id | NUMBER(19) | Long | 自动 | 主键(雪花算法,BaseModel 提供) |
|
|
36
|
+
| — | created_by | VARCHAR2(64) | String | 自动 | 创建人(TimeModel 提供) |
|
|
37
|
+
| — | created_date | TIMESTAMP | LocalDateTime | 自动 | 创建时间(TimeModel 提供) |
|
|
38
|
+
| — | last_updated_by | VARCHAR2(64) | String | 自动 | 更新人(TimeModel 提供) |
|
|
39
|
+
| — | last_updated_date | TIMESTAMP | LocalDateTime | 自动 | 更新时间(TimeModel 提供) |
|
|
40
|
+
| | | | | | |
|
|
41
|
+
| is_effect | is_effect | NUMBER(1) | Integer | 是 | 启用状态(1=启用, 0=停用) |
|
|
42
|
+
|
|
43
|
+
> 请补充业务字段。审计字段(created_by 等)和主键(id)由 BaseModel 自动提供,无需在 Entity 中声明。
|
|
44
|
+
|
|
45
|
+
## 业务规则
|
|
46
|
+
|
|
47
|
+
1.
|
|
48
|
+
2.
|
|
49
|
+
3.
|
|
50
|
+
|
|
51
|
+
## 查询条件
|
|
52
|
+
|
|
53
|
+
| 字段 | 查询方式 | 说明 |
|
|
54
|
+
|------|---------|------|
|
|
55
|
+
| | LIKE / = / BETWEEN / IN | |
|
|
56
|
+
|
|
57
|
+
## 非目标
|
|
58
|
+
|
|
59
|
+
- 不包含:
|
|
60
|
+
|
|
61
|
+
## 关联模块
|
|
62
|
+
|
|
63
|
+
- 依赖:
|
|
64
|
+
- 被依赖:
|