@downcity/services 0.1.114 → 0.1.119

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.
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Feedback 服务子模块公共入口。
3
+ */
4
+ export { FeedbackService } from "./service.js";
5
+ export { feedbackMessages } from "./schema.js";
6
+ export type { FeedbackCreateInput, FeedbackCreateResult, FeedbackMessage, FeedbackQueryInput, FeedbackReplyInput, FeedbackReplyResult, FeedbackStatus, FeedbackStatusUpdateInput, FeedbackStatusUpdateResult, } from "./types.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/feedback/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,YAAY,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Feedback 服务子模块公共入口。
3
+ */
4
+ export { FeedbackService } from "./service.js";
5
+ export { feedbackMessages } from "./schema.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/feedback/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Feedback 服务 HTTP 路由装配模块。
3
+ *
4
+ * 关键说明(中文)
5
+ * - 这里只负责把公开 action 映射到 HTTP 路由
6
+ * - 反馈创建、查询、答复和状态更新全部收敛在 FeedbackService 内部
7
+ */
8
+ import { type ServiceInstallContext } from "@downcity/city";
9
+ import type { FeedbackService } from "./service.js";
10
+ /**
11
+ * 注册 Feedback 服务的 HTTP 路由。
12
+ */
13
+ export declare function registerFeedbackRoutes(service: FeedbackService, ctx: ServiceInstallContext): void;
14
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/feedback/routes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAa,KAAK,qBAAqB,EAA4B,MAAM,gBAAgB,CAAC;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAQpD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,qBAAqB,GAAG,IAAI,CAoDjG"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Feedback 服务 HTTP 路由装配模块。
3
+ *
4
+ * 关键说明(中文)
5
+ * - 这里只负责把公开 action 映射到 HTTP 路由
6
+ * - 反馈创建、查询、答复和状态更新全部收敛在 FeedbackService 内部
7
+ */
8
+ import { httpError } from "@downcity/city";
9
+ /**
10
+ * 注册 Feedback 服务的 HTTP 路由。
11
+ */
12
+ export function registerFeedbackRoutes(service, ctx) {
13
+ ctx.route({
14
+ method: "POST",
15
+ path: "/send",
16
+ auth: ["user"],
17
+ handler: async (c) => {
18
+ const body = await c.json();
19
+ return c.jsonResponse(await service.create(readUserId(c), readCityId(c), body));
20
+ },
21
+ });
22
+ ctx.route({
23
+ method: "GET",
24
+ path: "/me",
25
+ auth: ["user"],
26
+ handler: async (c) => {
27
+ const input = await c.json();
28
+ return c.jsonResponse({
29
+ items: await service.listUserMessages(readUserId(c), readCityId(c), input),
30
+ });
31
+ },
32
+ });
33
+ ctx.route({
34
+ method: "GET",
35
+ path: "/messages",
36
+ auth: ["admin"],
37
+ handler: async (c) => {
38
+ const input = await c.json();
39
+ return c.jsonResponse({ items: await service.listMessages(input) });
40
+ },
41
+ });
42
+ ctx.route({
43
+ method: "POST",
44
+ path: "/reply",
45
+ auth: ["admin"],
46
+ handler: async (c) => {
47
+ const body = await c.json();
48
+ return c.jsonResponse(await service.reply(body));
49
+ },
50
+ });
51
+ ctx.route({
52
+ method: "POST",
53
+ path: "/status",
54
+ auth: ["admin"],
55
+ handler: async (c) => {
56
+ const body = await c.json();
57
+ return c.jsonResponse(await service.updateStatus(body));
58
+ },
59
+ });
60
+ }
61
+ /**
62
+ * 读取当前用户 ID。
63
+ */
64
+ function readUserId(ctx) {
65
+ const user_id = ctx.user?.user_id ?? "";
66
+ if (!user_id)
67
+ throw httpError(401, "user token required");
68
+ return user_id;
69
+ }
70
+ /**
71
+ * 读取当前 city ID。
72
+ */
73
+ function readCityId(ctx) {
74
+ const city_id = ctx.city?.city_id ?? "";
75
+ if (!city_id)
76
+ throw httpError(401, "city token required");
77
+ return city_id;
78
+ }
79
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/feedback/routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Feedback 服务数据表结构。
3
+ *
4
+ * 关键说明(中文)
5
+ * - 本期反馈能力使用单表模型,便于官方和其它 city 复用
6
+ * - 所有字段保持 not null,可空语义统一用空字符串表达
7
+ * - metadata_json 保存客户端页面、版本、浏览器等上下文信息
8
+ */
9
+ /**
10
+ * 用户反馈消息表。
11
+ */
12
+ export declare const feedbackMessages: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
13
+ name: "service_feedback_messages";
14
+ schema: undefined;
15
+ columns: {
16
+ feedback_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
17
+ name: "feedback_id";
18
+ tableName: "service_feedback_messages";
19
+ dataType: "string";
20
+ columnType: "SQLiteText";
21
+ data: string;
22
+ driverParam: string;
23
+ notNull: true;
24
+ hasDefault: false;
25
+ isPrimaryKey: true;
26
+ isAutoincrement: false;
27
+ hasRuntimeDefault: false;
28
+ enumValues: [string, ...string[]];
29
+ baseColumn: never;
30
+ identity: undefined;
31
+ generated: undefined;
32
+ }, {}, {
33
+ length: number | undefined;
34
+ }>;
35
+ city_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
36
+ name: "city_id";
37
+ tableName: "service_feedback_messages";
38
+ dataType: "string";
39
+ columnType: "SQLiteText";
40
+ data: string;
41
+ driverParam: string;
42
+ notNull: true;
43
+ hasDefault: false;
44
+ isPrimaryKey: false;
45
+ isAutoincrement: false;
46
+ hasRuntimeDefault: false;
47
+ enumValues: [string, ...string[]];
48
+ baseColumn: never;
49
+ identity: undefined;
50
+ generated: undefined;
51
+ }, {}, {
52
+ length: number | undefined;
53
+ }>;
54
+ user_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
55
+ name: "user_id";
56
+ tableName: "service_feedback_messages";
57
+ dataType: "string";
58
+ columnType: "SQLiteText";
59
+ data: string;
60
+ driverParam: string;
61
+ notNull: true;
62
+ hasDefault: false;
63
+ isPrimaryKey: false;
64
+ isAutoincrement: false;
65
+ hasRuntimeDefault: false;
66
+ enumValues: [string, ...string[]];
67
+ baseColumn: never;
68
+ identity: undefined;
69
+ generated: undefined;
70
+ }, {}, {
71
+ length: number | undefined;
72
+ }>;
73
+ message: import("drizzle-orm/sqlite-core").SQLiteColumn<{
74
+ name: "message";
75
+ tableName: "service_feedback_messages";
76
+ dataType: "string";
77
+ columnType: "SQLiteText";
78
+ data: string;
79
+ driverParam: string;
80
+ notNull: true;
81
+ hasDefault: false;
82
+ isPrimaryKey: false;
83
+ isAutoincrement: false;
84
+ hasRuntimeDefault: false;
85
+ enumValues: [string, ...string[]];
86
+ baseColumn: never;
87
+ identity: undefined;
88
+ generated: undefined;
89
+ }, {}, {
90
+ length: number | undefined;
91
+ }>;
92
+ contact: import("drizzle-orm/sqlite-core").SQLiteColumn<{
93
+ name: "contact";
94
+ tableName: "service_feedback_messages";
95
+ dataType: "string";
96
+ columnType: "SQLiteText";
97
+ data: string;
98
+ driverParam: string;
99
+ notNull: true;
100
+ hasDefault: false;
101
+ isPrimaryKey: false;
102
+ isAutoincrement: false;
103
+ hasRuntimeDefault: false;
104
+ enumValues: [string, ...string[]];
105
+ baseColumn: never;
106
+ identity: undefined;
107
+ generated: undefined;
108
+ }, {}, {
109
+ length: number | undefined;
110
+ }>;
111
+ status: import("drizzle-orm/sqlite-core").SQLiteColumn<{
112
+ name: "status";
113
+ tableName: "service_feedback_messages";
114
+ dataType: "string";
115
+ columnType: "SQLiteText";
116
+ data: string;
117
+ driverParam: string;
118
+ notNull: true;
119
+ hasDefault: false;
120
+ isPrimaryKey: false;
121
+ isAutoincrement: false;
122
+ hasRuntimeDefault: false;
123
+ enumValues: [string, ...string[]];
124
+ baseColumn: never;
125
+ identity: undefined;
126
+ generated: undefined;
127
+ }, {}, {
128
+ length: number | undefined;
129
+ }>;
130
+ reply: import("drizzle-orm/sqlite-core").SQLiteColumn<{
131
+ name: "reply";
132
+ tableName: "service_feedback_messages";
133
+ dataType: "string";
134
+ columnType: "SQLiteText";
135
+ data: string;
136
+ driverParam: string;
137
+ notNull: true;
138
+ hasDefault: false;
139
+ isPrimaryKey: false;
140
+ isAutoincrement: false;
141
+ hasRuntimeDefault: false;
142
+ enumValues: [string, ...string[]];
143
+ baseColumn: never;
144
+ identity: undefined;
145
+ generated: undefined;
146
+ }, {}, {
147
+ length: number | undefined;
148
+ }>;
149
+ reply_by: import("drizzle-orm/sqlite-core").SQLiteColumn<{
150
+ name: "reply_by";
151
+ tableName: "service_feedback_messages";
152
+ dataType: "string";
153
+ columnType: "SQLiteText";
154
+ data: string;
155
+ driverParam: string;
156
+ notNull: true;
157
+ hasDefault: false;
158
+ isPrimaryKey: false;
159
+ isAutoincrement: false;
160
+ hasRuntimeDefault: false;
161
+ enumValues: [string, ...string[]];
162
+ baseColumn: never;
163
+ identity: undefined;
164
+ generated: undefined;
165
+ }, {}, {
166
+ length: number | undefined;
167
+ }>;
168
+ replied_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
169
+ name: "replied_at";
170
+ tableName: "service_feedback_messages";
171
+ dataType: "string";
172
+ columnType: "SQLiteText";
173
+ data: string;
174
+ driverParam: string;
175
+ notNull: true;
176
+ hasDefault: false;
177
+ isPrimaryKey: false;
178
+ isAutoincrement: false;
179
+ hasRuntimeDefault: false;
180
+ enumValues: [string, ...string[]];
181
+ baseColumn: never;
182
+ identity: undefined;
183
+ generated: undefined;
184
+ }, {}, {
185
+ length: number | undefined;
186
+ }>;
187
+ metadata_json: import("drizzle-orm/sqlite-core").SQLiteColumn<{
188
+ name: "metadata_json";
189
+ tableName: "service_feedback_messages";
190
+ dataType: "string";
191
+ columnType: "SQLiteText";
192
+ data: string;
193
+ driverParam: string;
194
+ notNull: true;
195
+ hasDefault: false;
196
+ isPrimaryKey: false;
197
+ isAutoincrement: false;
198
+ hasRuntimeDefault: false;
199
+ enumValues: [string, ...string[]];
200
+ baseColumn: never;
201
+ identity: undefined;
202
+ generated: undefined;
203
+ }, {}, {
204
+ length: number | undefined;
205
+ }>;
206
+ created_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
207
+ name: "created_at";
208
+ tableName: "service_feedback_messages";
209
+ dataType: "string";
210
+ columnType: "SQLiteText";
211
+ data: string;
212
+ driverParam: string;
213
+ notNull: true;
214
+ hasDefault: false;
215
+ isPrimaryKey: false;
216
+ isAutoincrement: false;
217
+ hasRuntimeDefault: false;
218
+ enumValues: [string, ...string[]];
219
+ baseColumn: never;
220
+ identity: undefined;
221
+ generated: undefined;
222
+ }, {}, {
223
+ length: number | undefined;
224
+ }>;
225
+ updated_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
226
+ name: "updated_at";
227
+ tableName: "service_feedback_messages";
228
+ dataType: "string";
229
+ columnType: "SQLiteText";
230
+ data: string;
231
+ driverParam: string;
232
+ notNull: true;
233
+ hasDefault: false;
234
+ isPrimaryKey: false;
235
+ isAutoincrement: false;
236
+ hasRuntimeDefault: false;
237
+ enumValues: [string, ...string[]];
238
+ baseColumn: never;
239
+ identity: undefined;
240
+ generated: undefined;
241
+ }, {}, {
242
+ length: number | undefined;
243
+ }>;
244
+ };
245
+ dialect: "sqlite";
246
+ }>;
247
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/feedback/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAa3B,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Feedback 服务数据表结构。
3
+ *
4
+ * 关键说明(中文)
5
+ * - 本期反馈能力使用单表模型,便于官方和其它 city 复用
6
+ * - 所有字段保持 not null,可空语义统一用空字符串表达
7
+ * - metadata_json 保存客户端页面、版本、浏览器等上下文信息
8
+ */
9
+ import { sqliteTable, text } from "drizzle-orm/sqlite-core";
10
+ /**
11
+ * 用户反馈消息表。
12
+ */
13
+ export const feedbackMessages = sqliteTable("service_feedback_messages", {
14
+ feedback_id: text("feedback_id").primaryKey(),
15
+ city_id: text("city_id").notNull(),
16
+ user_id: text("user_id").notNull(),
17
+ message: text("message").notNull(),
18
+ contact: text("contact").notNull(),
19
+ status: text("status").notNull(),
20
+ reply: text("reply").notNull(),
21
+ reply_by: text("reply_by").notNull(),
22
+ replied_at: text("replied_at").notNull(),
23
+ metadata_json: text("metadata_json").notNull(),
24
+ created_at: text("created_at").notNull(),
25
+ updated_at: text("updated_at").notNull(),
26
+ });
27
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/feedback/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAE5D;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC,2BAA2B,EAAE;IACvE,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,UAAU,EAAE;IAC7C,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IAClC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IAClC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IAClC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IAClC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE;IAChC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;IAC9B,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE;IACpC,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IACxC,aAAa,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE;IAC9C,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IACxC,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;CACzC,CAAC,CAAC"}
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Downcity 官方 Feedback 服务实现。
3
+ *
4
+ * 设计边界:
5
+ * - 用户反馈是 city 内用户提交给官方/管理员的单条消息
6
+ * - 服务只负责落库、查询、答复与状态更新,不做通知推送
7
+ * - official edge-worker 只需要显式注册本服务,不需要实现私有反馈逻辑
8
+ */
9
+ import { InstallableService, type ServiceInstallContext } from "@downcity/city";
10
+ import type { FeedbackCreateInput, FeedbackCreateResult, FeedbackMessage, FeedbackQueryInput, FeedbackReplyInput, FeedbackReplyResult, FeedbackStatusUpdateInput, FeedbackStatusUpdateResult } from "./types.js";
11
+ /**
12
+ * Feedback 服务实例。
13
+ */
14
+ export declare class FeedbackService extends InstallableService {
15
+ readonly id = "feedback";
16
+ readonly name = "Feedback";
17
+ readonly version = "0.1.0";
18
+ readonly schema: {
19
+ messages: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
20
+ name: "service_feedback_messages";
21
+ schema: undefined;
22
+ columns: {
23
+ feedback_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
24
+ name: "feedback_id";
25
+ tableName: "service_feedback_messages";
26
+ dataType: "string";
27
+ columnType: "SQLiteText";
28
+ data: string;
29
+ driverParam: string;
30
+ notNull: true;
31
+ hasDefault: false;
32
+ isPrimaryKey: true;
33
+ isAutoincrement: false;
34
+ hasRuntimeDefault: false;
35
+ enumValues: [string, ...string[]];
36
+ baseColumn: never;
37
+ identity: undefined;
38
+ generated: undefined;
39
+ }, {}, {
40
+ length: number | undefined;
41
+ }>;
42
+ city_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
43
+ name: "city_id";
44
+ tableName: "service_feedback_messages";
45
+ dataType: "string";
46
+ columnType: "SQLiteText";
47
+ data: string;
48
+ driverParam: string;
49
+ notNull: true;
50
+ hasDefault: false;
51
+ isPrimaryKey: false;
52
+ isAutoincrement: false;
53
+ hasRuntimeDefault: false;
54
+ enumValues: [string, ...string[]];
55
+ baseColumn: never;
56
+ identity: undefined;
57
+ generated: undefined;
58
+ }, {}, {
59
+ length: number | undefined;
60
+ }>;
61
+ user_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
62
+ name: "user_id";
63
+ tableName: "service_feedback_messages";
64
+ dataType: "string";
65
+ columnType: "SQLiteText";
66
+ data: string;
67
+ driverParam: string;
68
+ notNull: true;
69
+ hasDefault: false;
70
+ isPrimaryKey: false;
71
+ isAutoincrement: false;
72
+ hasRuntimeDefault: false;
73
+ enumValues: [string, ...string[]];
74
+ baseColumn: never;
75
+ identity: undefined;
76
+ generated: undefined;
77
+ }, {}, {
78
+ length: number | undefined;
79
+ }>;
80
+ message: import("drizzle-orm/sqlite-core").SQLiteColumn<{
81
+ name: "message";
82
+ tableName: "service_feedback_messages";
83
+ dataType: "string";
84
+ columnType: "SQLiteText";
85
+ data: string;
86
+ driverParam: string;
87
+ notNull: true;
88
+ hasDefault: false;
89
+ isPrimaryKey: false;
90
+ isAutoincrement: false;
91
+ hasRuntimeDefault: false;
92
+ enumValues: [string, ...string[]];
93
+ baseColumn: never;
94
+ identity: undefined;
95
+ generated: undefined;
96
+ }, {}, {
97
+ length: number | undefined;
98
+ }>;
99
+ contact: import("drizzle-orm/sqlite-core").SQLiteColumn<{
100
+ name: "contact";
101
+ tableName: "service_feedback_messages";
102
+ dataType: "string";
103
+ columnType: "SQLiteText";
104
+ data: string;
105
+ driverParam: string;
106
+ notNull: true;
107
+ hasDefault: false;
108
+ isPrimaryKey: false;
109
+ isAutoincrement: false;
110
+ hasRuntimeDefault: false;
111
+ enumValues: [string, ...string[]];
112
+ baseColumn: never;
113
+ identity: undefined;
114
+ generated: undefined;
115
+ }, {}, {
116
+ length: number | undefined;
117
+ }>;
118
+ status: import("drizzle-orm/sqlite-core").SQLiteColumn<{
119
+ name: "status";
120
+ tableName: "service_feedback_messages";
121
+ dataType: "string";
122
+ columnType: "SQLiteText";
123
+ data: string;
124
+ driverParam: string;
125
+ notNull: true;
126
+ hasDefault: false;
127
+ isPrimaryKey: false;
128
+ isAutoincrement: false;
129
+ hasRuntimeDefault: false;
130
+ enumValues: [string, ...string[]];
131
+ baseColumn: never;
132
+ identity: undefined;
133
+ generated: undefined;
134
+ }, {}, {
135
+ length: number | undefined;
136
+ }>;
137
+ reply: import("drizzle-orm/sqlite-core").SQLiteColumn<{
138
+ name: "reply";
139
+ tableName: "service_feedback_messages";
140
+ dataType: "string";
141
+ columnType: "SQLiteText";
142
+ data: string;
143
+ driverParam: string;
144
+ notNull: true;
145
+ hasDefault: false;
146
+ isPrimaryKey: false;
147
+ isAutoincrement: false;
148
+ hasRuntimeDefault: false;
149
+ enumValues: [string, ...string[]];
150
+ baseColumn: never;
151
+ identity: undefined;
152
+ generated: undefined;
153
+ }, {}, {
154
+ length: number | undefined;
155
+ }>;
156
+ reply_by: import("drizzle-orm/sqlite-core").SQLiteColumn<{
157
+ name: "reply_by";
158
+ tableName: "service_feedback_messages";
159
+ dataType: "string";
160
+ columnType: "SQLiteText";
161
+ data: string;
162
+ driverParam: string;
163
+ notNull: true;
164
+ hasDefault: false;
165
+ isPrimaryKey: false;
166
+ isAutoincrement: false;
167
+ hasRuntimeDefault: false;
168
+ enumValues: [string, ...string[]];
169
+ baseColumn: never;
170
+ identity: undefined;
171
+ generated: undefined;
172
+ }, {}, {
173
+ length: number | undefined;
174
+ }>;
175
+ replied_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
176
+ name: "replied_at";
177
+ tableName: "service_feedback_messages";
178
+ dataType: "string";
179
+ columnType: "SQLiteText";
180
+ data: string;
181
+ driverParam: string;
182
+ notNull: true;
183
+ hasDefault: false;
184
+ isPrimaryKey: false;
185
+ isAutoincrement: false;
186
+ hasRuntimeDefault: false;
187
+ enumValues: [string, ...string[]];
188
+ baseColumn: never;
189
+ identity: undefined;
190
+ generated: undefined;
191
+ }, {}, {
192
+ length: number | undefined;
193
+ }>;
194
+ metadata_json: import("drizzle-orm/sqlite-core").SQLiteColumn<{
195
+ name: "metadata_json";
196
+ tableName: "service_feedback_messages";
197
+ dataType: "string";
198
+ columnType: "SQLiteText";
199
+ data: string;
200
+ driverParam: string;
201
+ notNull: true;
202
+ hasDefault: false;
203
+ isPrimaryKey: false;
204
+ isAutoincrement: false;
205
+ hasRuntimeDefault: false;
206
+ enumValues: [string, ...string[]];
207
+ baseColumn: never;
208
+ identity: undefined;
209
+ generated: undefined;
210
+ }, {}, {
211
+ length: number | undefined;
212
+ }>;
213
+ created_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
214
+ name: "created_at";
215
+ tableName: "service_feedback_messages";
216
+ dataType: "string";
217
+ columnType: "SQLiteText";
218
+ data: string;
219
+ driverParam: string;
220
+ notNull: true;
221
+ hasDefault: false;
222
+ isPrimaryKey: false;
223
+ isAutoincrement: false;
224
+ hasRuntimeDefault: false;
225
+ enumValues: [string, ...string[]];
226
+ baseColumn: never;
227
+ identity: undefined;
228
+ generated: undefined;
229
+ }, {}, {
230
+ length: number | undefined;
231
+ }>;
232
+ updated_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
233
+ name: "updated_at";
234
+ tableName: "service_feedback_messages";
235
+ dataType: "string";
236
+ columnType: "SQLiteText";
237
+ data: string;
238
+ driverParam: string;
239
+ notNull: true;
240
+ hasDefault: false;
241
+ isPrimaryKey: false;
242
+ isAutoincrement: false;
243
+ hasRuntimeDefault: false;
244
+ enumValues: [string, ...string[]];
245
+ baseColumn: never;
246
+ identity: undefined;
247
+ generated: undefined;
248
+ }, {}, {
249
+ length: number | undefined;
250
+ }>;
251
+ };
252
+ dialect: "sqlite";
253
+ }>;
254
+ };
255
+ private messages_table?;
256
+ constructor();
257
+ install(ctx: ServiceInstallContext): void;
258
+ /**
259
+ * 创建用户反馈。
260
+ */
261
+ create(user_id: string, city_id: string, input: FeedbackCreateInput): Promise<FeedbackCreateResult>;
262
+ /**
263
+ * 管理员查询反馈。
264
+ */
265
+ listMessages(input: FeedbackQueryInput): Promise<FeedbackMessage[]>;
266
+ /**
267
+ * 查询当前用户在当前 city 下提交的反馈。
268
+ */
269
+ listUserMessages(user_id: string, city_id: string, input: FeedbackQueryInput): Promise<FeedbackMessage[]>;
270
+ /**
271
+ * 写入管理员答复。
272
+ */
273
+ reply(input: FeedbackReplyInput): Promise<FeedbackReplyResult>;
274
+ /**
275
+ * 更新反馈处理状态。
276
+ */
277
+ updateStatus(input: FeedbackStatusUpdateInput): Promise<FeedbackStatusUpdateResult>;
278
+ /**
279
+ * 读取反馈,缺失时返回 404。
280
+ */
281
+ private readRequired;
282
+ /**
283
+ * 读取反馈消息表。
284
+ */
285
+ private messages;
286
+ }
287
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/feedback/service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,kBAAkB,EAGlB,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,EACzB,0BAA0B,EAC3B,MAAM,YAAY,CAAC;AAepB;;GAEG;AACH,qBAAa,eAAgB,SAAQ,kBAAkB;IACrD,QAAQ,CAAC,EAAE,cAAc;IACzB,QAAQ,CAAC,IAAI,cAAc;IAC3B,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAEb;IAEF,OAAO,CAAC,cAAc,CAAC,CAAgC;;IAYvD,OAAO,CAAC,GAAG,EAAE,qBAAqB,GAAG,IAAI;IAKzC;;OAEG;IACG,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA4BzG;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAczE;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAa/G;;OAEG;IACG,KAAK,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAyBpE;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAqBzF;;OAEG;YACW,YAAY;IAO1B;;OAEG;IACH,OAAO,CAAC,QAAQ;CAIjB"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Downcity 官方 Feedback 服务实现。
3
+ *
4
+ * 设计边界:
5
+ * - 用户反馈是 city 内用户提交给官方/管理员的单条消息
6
+ * - 服务只负责落库、查询、答复与状态更新,不做通知推送
7
+ * - official edge-worker 只需要显式注册本服务,不需要实现私有反馈逻辑
8
+ */
9
+ import { InstallableService, httpError, } from "@downcity/city";
10
+ import { registerFeedbackRoutes } from "./routes.js";
11
+ import { feedbackMessages } from "./schema.js";
12
+ import { normalizeFeedbackStatus, normalizeLimit, normalizeOptionalFeedbackStatus, normalizeOptionalFilter, parseFeedbackMessage, randomFeedbackId, readFeedbackId, readOptionalText, readRequiredText, sortAndLimitFeedback, stringifyFeedbackMeta, } from "./utils.js";
13
+ /**
14
+ * Feedback 服务实例。
15
+ */
16
+ export class FeedbackService extends InstallableService {
17
+ id = "feedback";
18
+ name = "Feedback";
19
+ version = "0.1.0";
20
+ schema = {
21
+ messages: feedbackMessages,
22
+ };
23
+ messages_table;
24
+ constructor() {
25
+ super();
26
+ this.instruction = [
27
+ "允许已登录用户提交产品反馈、问题报告和建议。",
28
+ "管理员可查询反馈、写入官方答复,并更新 open / reviewing / replied / closed 状态。",
29
+ "用户可查看自己在当前 city 提交过的反馈和官方答复。",
30
+ "反馈默认只保存在 Downcity 数据库,不会自动发送到邮件、Slack 或其它外部渠道。",
31
+ ].join("\n");
32
+ }
33
+ install(ctx) {
34
+ this.messages_table = ctx.table("messages");
35
+ registerFeedbackRoutes(this, ctx);
36
+ }
37
+ /**
38
+ * 创建用户反馈。
39
+ */
40
+ async create(user_id, city_id, input) {
41
+ const message = readRequiredText(input.message, "message", 10_000);
42
+ const contact = readOptionalText(input.contact, "contact", 500);
43
+ const now = new Date().toISOString();
44
+ const item = {
45
+ feedback_id: randomFeedbackId(),
46
+ city_id: readRequiredText(city_id, "city_id", 500),
47
+ user_id: readRequiredText(user_id, "user_id", 500),
48
+ message,
49
+ contact,
50
+ status: "open",
51
+ reply: "",
52
+ reply_by: "",
53
+ replied_at: "",
54
+ metadata_json: stringifyFeedbackMeta(input.meta),
55
+ created_at: now,
56
+ updated_at: now,
57
+ };
58
+ await this.messages().insert(item);
59
+ return {
60
+ feedback_id: item.feedback_id,
61
+ status: item.status,
62
+ created_at: item.created_at,
63
+ };
64
+ }
65
+ /**
66
+ * 管理员查询反馈。
67
+ */
68
+ async listMessages(input) {
69
+ const city_id = normalizeOptionalFilter(input.city_id, "city_id");
70
+ const user_id = normalizeOptionalFilter(input.user_id, "user_id");
71
+ const status = normalizeOptionalFeedbackStatus(input.status);
72
+ const limit = normalizeLimit(input.limit, 100, 500);
73
+ const where = {};
74
+ if (city_id)
75
+ where.city_id = city_id;
76
+ if (user_id)
77
+ where.user_id = user_id;
78
+ if (status)
79
+ where.status = status;
80
+ return sortAndLimitFeedback(await this.messages().select(where), limit);
81
+ }
82
+ /**
83
+ * 查询当前用户在当前 city 下提交的反馈。
84
+ */
85
+ async listUserMessages(user_id, city_id, input) {
86
+ const status = normalizeOptionalFeedbackStatus(input.status);
87
+ const limit = normalizeLimit(input.limit, 100, 200);
88
+ const where = {
89
+ user_id: readRequiredText(user_id, "user_id", 500),
90
+ city_id: readRequiredText(city_id, "city_id", 500),
91
+ };
92
+ if (status)
93
+ where.status = status;
94
+ return sortAndLimitFeedback(await this.messages().select(where), limit);
95
+ }
96
+ /**
97
+ * 写入管理员答复。
98
+ */
99
+ async reply(input) {
100
+ const feedback_id = readFeedbackId(input.feedback_id);
101
+ const reply = readRequiredText(input.reply, "reply", 10_000);
102
+ const reply_by = readOptionalText(input.reply_by, "reply_by", 200, "admin") || "admin";
103
+ await this.readRequired(feedback_id);
104
+ const replied_at = new Date().toISOString();
105
+ await this.messages().update({
106
+ where: { feedback_id },
107
+ values: {
108
+ reply,
109
+ reply_by,
110
+ replied_at,
111
+ status: "replied",
112
+ updated_at: replied_at,
113
+ },
114
+ });
115
+ return {
116
+ feedback_id,
117
+ status: "replied",
118
+ replied_at,
119
+ };
120
+ }
121
+ /**
122
+ * 更新反馈处理状态。
123
+ */
124
+ async updateStatus(input) {
125
+ const feedback_id = readFeedbackId(input.feedback_id);
126
+ const status = normalizeFeedbackStatus(input.status);
127
+ await this.readRequired(feedback_id);
128
+ const updated_at = new Date().toISOString();
129
+ await this.messages().update({
130
+ where: { feedback_id },
131
+ values: {
132
+ status,
133
+ updated_at,
134
+ },
135
+ });
136
+ return {
137
+ feedback_id,
138
+ status,
139
+ updated_at,
140
+ };
141
+ }
142
+ /**
143
+ * 读取反馈,缺失时返回 404。
144
+ */
145
+ async readRequired(feedback_id) {
146
+ const rows = await this.messages().select({ feedback_id });
147
+ const row = rows[0];
148
+ if (!row)
149
+ throw httpError(404, "feedback not found");
150
+ return parseFeedbackMessage(row);
151
+ }
152
+ /**
153
+ * 读取反馈消息表。
154
+ */
155
+ messages() {
156
+ if (!this.messages_table)
157
+ throw new Error("FeedbackService messages table is not ready");
158
+ return this.messages_table;
159
+ }
160
+ }
161
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/feedback/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Feedback 服务公共类型。
3
+ *
4
+ * 关键说明(中文)
5
+ * - 类型集中放在本模块,避免 service/routes/utils 之间互相定义结构
6
+ * - 输入类型使用 unknown 承接 HTTP 边界,统一由 utils 做校验与标准化
7
+ * - 对外返回类型保持 snake_case,与数据库字段和 HTTP JSON 字段一致
8
+ */
9
+ /**
10
+ * 反馈处理状态。
11
+ */
12
+ export type FeedbackStatus = "open" | "reviewing" | "replied" | "closed";
13
+ /**
14
+ * 反馈消息完整记录。
15
+ */
16
+ export interface FeedbackMessage extends Record<string, unknown> {
17
+ /** 反馈 ID。 */
18
+ feedback_id: string;
19
+ /** 反馈来源 city。 */
20
+ city_id: string;
21
+ /** 反馈提交用户。 */
22
+ user_id: string;
23
+ /** 用户反馈正文。 */
24
+ message: string;
25
+ /** 用户留下的联系方式,未填写时为空字符串。 */
26
+ contact: string;
27
+ /** 反馈处理状态。 */
28
+ status: FeedbackStatus;
29
+ /** 管理员答复内容,未答复时为空字符串。 */
30
+ reply: string;
31
+ /** 答复人标识,未答复时为空字符串。 */
32
+ reply_by: string;
33
+ /** 答复时间 ISO 字符串,未答复时为空字符串。 */
34
+ replied_at: string;
35
+ /** 附加上下文 JSON 字符串。 */
36
+ metadata_json: string;
37
+ /** 创建时间 ISO 字符串。 */
38
+ created_at: string;
39
+ /** 最近更新时间 ISO 字符串。 */
40
+ updated_at: string;
41
+ }
42
+ /**
43
+ * 用户提交反馈入参。
44
+ */
45
+ export interface FeedbackCreateInput extends Record<string, unknown> {
46
+ /** 用户反馈正文。 */
47
+ message: unknown;
48
+ /** 可选联系方式,例如邮箱、微信、Discord。 */
49
+ contact?: unknown;
50
+ /** 可选上下文,例如页面路径、客户端版本、浏览器信息。 */
51
+ meta?: unknown;
52
+ }
53
+ /**
54
+ * 反馈查询入参。
55
+ */
56
+ export interface FeedbackQueryInput extends Record<string, unknown> {
57
+ /** 按 city 过滤。 */
58
+ city_id?: unknown;
59
+ /** 按用户过滤。 */
60
+ user_id?: unknown;
61
+ /** 按状态过滤。 */
62
+ status?: unknown;
63
+ /** 返回数量限制。 */
64
+ limit?: unknown;
65
+ }
66
+ /**
67
+ * 管理员答复反馈入参。
68
+ */
69
+ export interface FeedbackReplyInput extends Record<string, unknown> {
70
+ /** 要答复的反馈 ID。 */
71
+ feedback_id: unknown;
72
+ /** 管理员答复内容。 */
73
+ reply: unknown;
74
+ /** 可选答复人标识,未传时使用 admin。 */
75
+ reply_by?: unknown;
76
+ }
77
+ /**
78
+ * 管理员状态更新入参。
79
+ */
80
+ export interface FeedbackStatusUpdateInput extends Record<string, unknown> {
81
+ /** 要更新的反馈 ID。 */
82
+ feedback_id: unknown;
83
+ /** 目标处理状态。 */
84
+ status: unknown;
85
+ }
86
+ /**
87
+ * 用户提交反馈结果。
88
+ */
89
+ export interface FeedbackCreateResult {
90
+ /** 反馈 ID。 */
91
+ feedback_id: string;
92
+ /** 创建后的默认处理状态。 */
93
+ status: FeedbackStatus;
94
+ /** 创建时间 ISO 字符串。 */
95
+ created_at: string;
96
+ }
97
+ /**
98
+ * 管理员答复反馈结果。
99
+ */
100
+ export interface FeedbackReplyResult {
101
+ /** 反馈 ID。 */
102
+ feedback_id: string;
103
+ /** 答复后的处理状态。 */
104
+ status: FeedbackStatus;
105
+ /** 答复时间 ISO 字符串。 */
106
+ replied_at: string;
107
+ }
108
+ /**
109
+ * 管理员状态更新结果。
110
+ */
111
+ export interface FeedbackStatusUpdateResult {
112
+ /** 反馈 ID。 */
113
+ feedback_id: string;
114
+ /** 更新后的处理状态。 */
115
+ status: FeedbackStatus;
116
+ /** 最近更新时间 ISO 字符串。 */
117
+ updated_at: string;
118
+ }
119
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/feedback/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,WAAW,GACX,SAAS,GACT,QAAQ,CAAC;AAEb;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC9D,aAAa;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc;IACd,MAAM,EAAE,cAAc,CAAC;IACvB,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClE,cAAc;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gCAAgC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACjE,iBAAiB;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACjE,iBAAiB;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe;IACf,KAAK,EAAE,OAAO,CAAC;IACf,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACxE,iBAAiB;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,aAAa;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,oBAAoB;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,aAAa;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB;IAChB,MAAM,EAAE,cAAc,CAAC;IACvB,oBAAoB;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,aAAa;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB;IAChB,MAAM,EAAE,cAAc,CAAC;IACvB,sBAAsB;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Feedback 服务公共类型。
3
+ *
4
+ * 关键说明(中文)
5
+ * - 类型集中放在本模块,避免 service/routes/utils 之间互相定义结构
6
+ * - 输入类型使用 unknown 承接 HTTP 边界,统一由 utils 做校验与标准化
7
+ * - 对外返回类型保持 snake_case,与数据库字段和 HTTP JSON 字段一致
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/feedback/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Feedback 服务输入校验与数据整理工具。
3
+ *
4
+ * 关键说明(中文)
5
+ * - HTTP 边界传入 unknown,所有字段必须先经过这里标准化
6
+ * - 查询阶段使用 CityTableApi 的等值能力读取候选集,再在内存排序截断
7
+ * - JSON 上下文序列化失败时退化为 `{}`,避免用户反馈因为 meta 异常丢失
8
+ */
9
+ import type { FeedbackMessage, FeedbackStatus } from "./types.js";
10
+ /**
11
+ * 读取必填字符串。
12
+ */
13
+ export declare function readRequiredText(value: unknown, label: string, max_length: number): string;
14
+ /**
15
+ * 读取可选字符串。
16
+ */
17
+ export declare function readOptionalText(value: unknown, label: string, max_length: number, fallback?: string): string;
18
+ /**
19
+ * 读取反馈 ID。
20
+ */
21
+ export declare function readFeedbackId(value: unknown): string;
22
+ /**
23
+ * 标准化反馈处理状态。
24
+ */
25
+ export declare function normalizeFeedbackStatus(value: unknown): FeedbackStatus;
26
+ /**
27
+ * 标准化可选状态过滤条件。
28
+ */
29
+ export declare function normalizeOptionalFeedbackStatus(value: unknown): FeedbackStatus | undefined;
30
+ /**
31
+ * 标准化列表上限。
32
+ */
33
+ export declare function normalizeLimit(value: unknown, default_limit: number, max_limit: number): number;
34
+ /**
35
+ * 标准化可选等值过滤文本。
36
+ */
37
+ export declare function normalizeOptionalFilter(value: unknown, label: string): string | undefined;
38
+ /**
39
+ * 安全序列化反馈上下文。
40
+ */
41
+ export declare function stringifyFeedbackMeta(value: unknown): string;
42
+ /**
43
+ * 生成反馈 ID。
44
+ */
45
+ export declare function randomFeedbackId(): string;
46
+ /**
47
+ * 解析反馈消息行。
48
+ */
49
+ export declare function parseFeedbackMessage(row: FeedbackMessage): FeedbackMessage;
50
+ /**
51
+ * 按创建时间倒序排列并截断。
52
+ */
53
+ export declare function sortAndLimitFeedback(rows: FeedbackMessage[], limit: number): FeedbackMessage[];
54
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/feedback/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AASlE;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAK1F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,MAAM,CAIzG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAMtE;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAG1F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAI/F;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMzF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAO5D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAQzC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,eAAe,GAAG,eAAe,CAe1E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAK9F"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Feedback 服务输入校验与数据整理工具。
3
+ *
4
+ * 关键说明(中文)
5
+ * - HTTP 边界传入 unknown,所有字段必须先经过这里标准化
6
+ * - 查询阶段使用 CityTableApi 的等值能力读取候选集,再在内存排序截断
7
+ * - JSON 上下文序列化失败时退化为 `{}`,避免用户反馈因为 meta 异常丢失
8
+ */
9
+ import { httpError } from "@downcity/city";
10
+ const FEEDBACK_STATUSES = [
11
+ "open",
12
+ "reviewing",
13
+ "replied",
14
+ "closed",
15
+ ];
16
+ /**
17
+ * 读取必填字符串。
18
+ */
19
+ export function readRequiredText(value, label, max_length) {
20
+ const normalized = String(value ?? "").trim();
21
+ if (!normalized)
22
+ throw httpError(400, `${label} is required`);
23
+ if (normalized.length > max_length)
24
+ throw httpError(400, `${label} is too long`);
25
+ return normalized;
26
+ }
27
+ /**
28
+ * 读取可选字符串。
29
+ */
30
+ export function readOptionalText(value, label, max_length, fallback = "") {
31
+ const normalized = String(value ?? fallback).trim();
32
+ if (normalized.length > max_length)
33
+ throw httpError(400, `${label} is too long`);
34
+ return normalized;
35
+ }
36
+ /**
37
+ * 读取反馈 ID。
38
+ */
39
+ export function readFeedbackId(value) {
40
+ return readRequiredText(value, "feedback_id", 200);
41
+ }
42
+ /**
43
+ * 标准化反馈处理状态。
44
+ */
45
+ export function normalizeFeedbackStatus(value) {
46
+ const normalized = String(value ?? "").trim();
47
+ if (FEEDBACK_STATUSES.includes(normalized)) {
48
+ return normalized;
49
+ }
50
+ throw httpError(400, "status must be one of: open, reviewing, replied, closed");
51
+ }
52
+ /**
53
+ * 标准化可选状态过滤条件。
54
+ */
55
+ export function normalizeOptionalFeedbackStatus(value) {
56
+ if (value === undefined || value === null || String(value).trim() === "")
57
+ return undefined;
58
+ return normalizeFeedbackStatus(value);
59
+ }
60
+ /**
61
+ * 标准化列表上限。
62
+ */
63
+ export function normalizeLimit(value, default_limit, max_limit) {
64
+ const normalized = Number(value ?? default_limit);
65
+ if (!Number.isInteger(normalized) || normalized <= 0)
66
+ return default_limit;
67
+ return Math.min(normalized, max_limit);
68
+ }
69
+ /**
70
+ * 标准化可选等值过滤文本。
71
+ */
72
+ export function normalizeOptionalFilter(value, label) {
73
+ if (value === undefined || value === null)
74
+ return undefined;
75
+ const normalized = String(value).trim();
76
+ if (!normalized)
77
+ return undefined;
78
+ if (normalized.length > 500)
79
+ throw httpError(400, `${label} is too long`);
80
+ return normalized;
81
+ }
82
+ /**
83
+ * 安全序列化反馈上下文。
84
+ */
85
+ export function stringifyFeedbackMeta(value) {
86
+ try {
87
+ const serialized = JSON.stringify(value ?? {});
88
+ return serialized === undefined ? "{}" : serialized;
89
+ }
90
+ catch {
91
+ return "{}";
92
+ }
93
+ }
94
+ /**
95
+ * 生成反馈 ID。
96
+ */
97
+ export function randomFeedbackId() {
98
+ const bytes = new Uint8Array(12);
99
+ crypto.getRandomValues(bytes);
100
+ const suffix = btoa(String.fromCharCode(...bytes))
101
+ .replace(/\+/g, "-")
102
+ .replace(/\//g, "_")
103
+ .replace(/=+$/u, "");
104
+ return `fb_${suffix}`;
105
+ }
106
+ /**
107
+ * 解析反馈消息行。
108
+ */
109
+ export function parseFeedbackMessage(row) {
110
+ return {
111
+ feedback_id: String(row.feedback_id),
112
+ city_id: String(row.city_id),
113
+ user_id: String(row.user_id),
114
+ message: String(row.message),
115
+ contact: String(row.contact ?? ""),
116
+ status: normalizeFeedbackStatus(row.status),
117
+ reply: String(row.reply ?? ""),
118
+ reply_by: String(row.reply_by ?? ""),
119
+ replied_at: String(row.replied_at ?? ""),
120
+ metadata_json: String(row.metadata_json ?? "{}"),
121
+ created_at: String(row.created_at),
122
+ updated_at: String(row.updated_at),
123
+ };
124
+ }
125
+ /**
126
+ * 按创建时间倒序排列并截断。
127
+ */
128
+ export function sortAndLimitFeedback(rows, limit) {
129
+ return rows
130
+ .map(parseFeedbackMessage)
131
+ .sort((a, b) => b.created_at.localeCompare(a.created_at))
132
+ .slice(0, limit);
133
+ }
134
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/feedback/utils.ts"],"names":[],"mappings":"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"}
package/bin/index.d.ts CHANGED
@@ -20,6 +20,8 @@ export type { Credits, } from "./types/Amount.js";
20
20
  export { creemPaymentProvider, dodoPaymentProvider, PaymentService, stripePaymentProvider, waffoPaymentProvider, } from "./payment/index.js";
21
21
  export type { PaymentCheckoutCreateResult, PaymentCreateCheckoutInput, PaymentEventRecord, PaymentEventSyncStatus, PaymentMethodItem, PaymentMethodReason, PaymentMethodType, PaymentProvider, PaymentProviderCheckoutInput, PaymentProviderCheckoutResult, PaymentProviderContext, PaymentProviderWebhookEvent, PaymentProviderWebhookInput, PaymentRecord, PaymentServiceOptions, PaymentTopupRecord, PaymentStatus, } from "./payment/types.js";
22
22
  export type { CreemPaymentProviderOptions, DodoPaymentProviderOptions, StripePaymentProviderOptions, WaffoPaymentProviderOptions, } from "./payment/types.js";
23
+ export { FeedbackService, feedbackMessages } from "./feedback/index.js";
24
+ export type { FeedbackCreateInput, FeedbackCreateResult, FeedbackMessage, FeedbackQueryInput, FeedbackReplyInput, FeedbackReplyResult, FeedbackStatus, FeedbackStatusUpdateInput, FeedbackStatusUpdateResult, } from "./feedback/index.js";
23
25
  export { usageEvents, UsageService } from "./usage/index.js";
24
26
  export type { UsageServiceOptions } from "./usage/index.js";
25
27
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAClE,YAAY,EACV,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,4BAA4B,EAC5B,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,4BAA4B,EAC5B,sBAAsB,EACtB,6BAA6B,EAC7B,uBAAuB,EACvB,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,OAAO,GACR,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,2BAA2B,EAC3B,0BAA0B,EAC1B,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,4BAA4B,EAC5B,6BAA6B,EAC7B,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAC3B,aAAa,EACd,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,2BAA2B,EAC3B,0BAA0B,EAC1B,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAClE,YAAY,EACV,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,4BAA4B,EAC5B,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,4BAA4B,EAC5B,sBAAsB,EACtB,6BAA6B,EAC7B,uBAAuB,EACvB,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,OAAO,GACR,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,2BAA2B,EAC3B,0BAA0B,EAC1B,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,4BAA4B,EAC5B,6BAA6B,EAC7B,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAC3B,aAAa,EACd,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,2BAA2B,EAC3B,0BAA0B,EAC1B,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACxE,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
package/bin/index.js CHANGED
@@ -14,5 +14,6 @@ export { balanceAccounts, balanceCharges, balanceLedger, balanceRedeemCodes, bal
14
14
  export { BalanceService } from "./balance/service.js";
15
15
  export { CREDITS_PER_USD, USD_DECIMAL_PLACES, } from "./types/Amount.js";
16
16
  export { creemPaymentProvider, dodoPaymentProvider, PaymentService, stripePaymentProvider, waffoPaymentProvider, } from "./payment/index.js";
17
+ export { FeedbackService, feedbackMessages } from "./feedback/index.js";
17
18
  export { usageEvents, UsageService } from "./usage/index.js";
18
19
  //# sourceMappingURL=index.js.map
package/bin/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAe7B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAwBtD,OAAO,EACL,eAAe,EACf,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AA2B5B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAe7B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAwBtD,OAAO,EACL,eAAe,EACf,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AA2B5B,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAaxE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@downcity/services",
3
- "version": "0.1.114",
3
+ "version": "0.1.119",
4
4
  "description": "Downcity public services package for accounts, balance, usage, Stripe, Creem, Dodo, and Waffo payment flows.",
5
5
  "type": "module",
6
6
  "main": "./bin/index.js",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@waffo/pancake-ts": "^0.11.0",
53
- "@downcity/city": "0.2.123",
53
+ "@downcity/city": "0.2.127",
54
54
  "better-auth": "^1.6.12",
55
55
  "dodopayments": "^2.36.0",
56
56
  "drizzle-orm": "^0.45.2"