diff --git a/doc/api.md b/doc/api.md index 98ce9a1..43015fe 100644 --- a/doc/api.md +++ b/doc/api.md @@ -643,4 +643,96 @@ geted: 0, // 是否已获得, 0: 未获得, 1: 已获得 } ] -``` \ No newline at end of file +``` + +### 17. 邮件列表 + +1. Method: POST +2. URI: /api/:accountid/mails + +| 字段 | 说明 | +| -------- | -------------------------------------- | +| accountid | 帐号id | + +> POST参数 + + +| 字段 | 说明 | +| -------- | -------------------------------------- | +| sid | 店铺id | + +3. Response: JSON + +```js +[{ + _id: '邮件id', + title: '邮件标题', + content: 1: '邮件正文' + status: 0, // 邮件状态: 0: 未读 1: 已读, 2: 已领取附件 + items: [{ + itemId: '物品的id' + name: '物品名', + count: 1 // 数量 + }] + }] +``` + +### 18. 设置邮件已读 + +1. Method: POST +2. URI: /api/:accountid/mail/read + +| 字段 | 说明 | +| -------- | -------------------------------------- | +| accountid | 帐号id | + +> POST参数 + + +| 字段 | 说明 | +| -------- | -------------------------------------- | +| ids | 邮件id数组 | + +### 19. 领取邮件附件 + +1. Method: POST +2. URI: /api/:accountid/mail/attachment + +| 字段 | 说明 | +| -------- | -------------------------------------- | +| accountid | 帐号id | + +> POST参数 + + +| 字段 | 说明 | +| -------- | -------------------------------------- | +| ids | 邮件id数组 | + +3. Response: JSON + +```js +[{ // 获得的物品列表 + coupon: '优惠券的id', + name: '优惠券名', + count: 1, //数量 + couponUrl: '优惠券详情图的url', + rewardType: 0, // 0: 优惠券, 1: 抽奖券 + }] +``` + +### 20. 删除邮件 + +1. Method: POST +2. URI: /api/:accountid/mail/delete + +| 字段 | 说明 | +| -------- | -------------------------------------- | +| accountid | 帐号id | + +> POST参数 + + +| 字段 | 说明 | +| -------- | -------------------------------------- | +| ids | 邮件id数组 | diff --git a/src/api/controllers/mail.controller.ts b/src/api/controllers/mail.controller.ts new file mode 100644 index 0000000..03f3e50 --- /dev/null +++ b/src/api/controllers/mail.controller.ts @@ -0,0 +1,83 @@ +import BaseController from '../../common/base.controller' +import { role, router } from '../../decorators/router' +import { UserMail } from '../../models/user/UserMail' +import { SysMail } from '../../models/content/SysMail' +import { ZError } from '../../common/ZError' +import { UserReward } from '../../models/user/UserReward' + +class MailController extends BaseController { + @role('anon') + @router('post /api/:accountId/mails') + async list(req: any) { + const { accountId, sid } = req.params + await UserMail.updateExpire(accountId) + let mails = await UserMail.find({ accountId, deleted: false }).sort({ status: 1, _id: -1 }) + let mailSet: Set = new Set() + for (let mail of mails) { + mailSet.add(mail.oid) + } + let sysMails = await SysMail.findMail(accountId, sid, [...mailSet]) + if (sysMails?.length > 0) { + for (let _m of sysMails) { + let mail = await UserMail.receiveOne(accountId, _m) + mails.push(mail) + } + } + return mails.map(o => o.toJson()) + } + + @role('anon') + @router('post /api/:accountId/mail/delete') + async delete(req: any) { + const { accountId, ids } = req.params + if (!ids || !Array.isArray(ids) || ids.length === 0) { + throw new ZError(10, 'ids必须是一个数组') + } + let result = await UserMail.deleteMails(accountId, ids) + return {} + } + + @role('anon') + @router('post /api/:accountId/mail/read') + async read(req: any) { + const { accountId, ids } = req.params + if (!ids || !Array.isArray(ids) || ids.length === 0) { + throw new ZError(10, 'ids必须是一个数组') + } + await UserMail.updateMails(accountId, ids, 1) + return {} + } + + @role('anon') + @router('post /api/:accountId/mail/attachment') + async attachment(req: any) { + const { accountId, ids } = req.params + if (!ids || !Array.isArray(ids) || ids.length === 0) { + throw new ZError(10, 'ids必须是一个数组') + } + let mails = await UserMail.find({ _id: { $in: ids }, deleted: false, accountId, status: { $lt: 2 } }) + if (!mails || mails.length === 0) { + throw new ZError(11, '未找到符合条件的邮件') + } + let results = [] + for (let mail of mails) { + if (mail.items && mail.items.length > 0) { + for (let item of mail.items) { + let record = await UserReward.saveOneRecord({ + accountId, + shop: mail.senderShop, + itemId: item.itemId, + count: item.count, + rewardId: mail._id + '', + activityId: mail.oid + '', + source: 'mail', + }) + results.push(record) + } + mail.status = 2 + await mail.save() + } + } + return results + } +} diff --git a/src/models/content/SysMail.ts b/src/models/content/SysMail.ts index 0c26ab0..f7c4e2a 100644 --- a/src/models/content/SysMail.ts +++ b/src/models/content/SysMail.ts @@ -1,18 +1,99 @@ import { dbconn } from '../../decorators/dbconn' -import { getModelForClass, modelOptions, prop } from '@typegoose/typegoose' +import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoose' import { Severity } from '@typegoose/typegoose/lib/internal/constants' import { BaseModule } from '../Base' +import { noJson } from '../../decorators/nojson' + +export class MailItemClass { + @prop() + public itemId: string + + @prop() + public name: string + + @prop() + public count: number +} @dbconn() +@index({ accounts: 1, deleted: 1, type: 1, sendTime: 1, endTime: 1 }, { unique: false }) +@index({ shops: 1, deleted: 1, type: 1, sendTime: 1, endTime: 1 }, { unique: false }) @modelOptions({ schemaOptions: { collection: 'sys_mail', timestamps: true }, options: { allowMixed: Severity.ALLOW }, }) export class SysMailClass extends BaseModule { + @prop() + public sender: string + + @prop() + public senderShop: string + @prop() public title: string @prop() public content: string + + /** + * 邮件类型 + * @type {number} 0: 普通邮件 1: 店铺群发 + */ + @prop({ default: 0 }) + public type: number + + @prop({ type: () => [String] }) + public accounts: string[] + + @prop({ type: () => [String] }) + public shops: string[] + + @prop({ type: () => [MailItemClass] }) + public items: MailItemClass[] + + /** + * 是否删除 + * @type {boolean} + */ + @noJson() + @prop({ default: false }) + public deleted: boolean + @noJson() + @prop() + public deleteTime: Date + + /** + * 创建人 + * @type {string} + */ + @prop() + public createdBy: string + + @prop() + public sendTime: number + + @prop() + public endTime: number + + public static async findMail(accountId: string, shop: string, excludes: string[]) { + let now = Date.now() + let mails0 = await SysMail.find({ + accounts: accountId, + type: 0, + deleted: false, + sendTime: { $lte: now }, + endTime: { $gte: now }, + _id: { $nin: excludes }, + }) + let mails1 = await SysMail.find({ + shops: shop, + type: 1, + deleted: false, + sendTime: { $lte: now }, + endTime: { $gte: now }, + _id: { $nin: excludes }, + }) + return mails0.concat(mails1) + } } export const SysMail = getModelForClass(SysMailClass, { existingConnection: SysMailClass.db }) diff --git a/src/models/user/UserMail.ts b/src/models/user/UserMail.ts new file mode 100644 index 0000000..2e28f50 --- /dev/null +++ b/src/models/user/UserMail.ts @@ -0,0 +1,94 @@ +import { dbconn } from '../../decorators/dbconn' +import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoose' +import { Severity } from '@typegoose/typegoose/lib/internal/constants' +import { BaseModule } from '../Base' +import { MailItemClass } from '../content/SysMail' +import { noJson } from '../../decorators/nojson' + +@dbconn() +@index({ accountId: 1, oid: 1 }, { unique: true }) +@index({ accountId: 1, deleted: 1, status: 1 }, { unique: false }) +@modelOptions({ + schemaOptions: { collection: 'user_mail', timestamps: true }, + options: { allowMixed: Severity.ALLOW }, +}) +export class UserMailClass extends BaseModule { + @prop() + public oid: string + + @prop() + public sender: string + + @prop() + public senderShop: string + + @prop() + public title: string + + @prop() + public content: string + + @prop() + public accountId: string + + @prop({ type: () => [MailItemClass] }) + public items: MailItemClass[] + + /** + * 邮件状态 + * @type {number} 0: 未读 1: 已读, 2: 已领取附件 + */ + @prop({ default: 0 }) + public status: number + + /** + * 过期时间 + * @type {number} + */ + @prop() + public expire: number + + /** + * 是否删除 + * @type {boolean} + */ + @noJson() + @prop({ default: false }) + public deleted: boolean + @noJson() + @prop() + public deleteTime: Date + + public static async receiveOne(accountId: string, mail: any) { + let record = new UserMail({}) + record.oid = mail.id + record.title = mail.title + record.content = mail.content + record.accountId = accountId + record.items = mail.items + record.sender = mail.sender + record.senderShop = mail.senderShop + record.expire = Date.now() + 3600 * 24 * 15 * 1000 + await record.save() + return record + } + + public static async updateExpire(accountId: string) { + await UserMail.updateMany( + { accountId, deleted: false, expire: { $lt: Date.now() } }, + { $set: { deleted: true, deleteTime: new Date() } }, + ) + } + + public static async deleteMails(accountId: string, ids: string[]) { + return UserMail.updateMany( + { accountId, deleted: false, _id: { $in: ids } }, + { $set: { deleted: true, deleteTime: new Date() } }, + ) + } + + public static async updateMails(accountId: string, ids: string[], status: number) { + return UserMail.updateMany({ accountId, deleted: false, _id: { $in: ids } }, { $set: { status: status } }) + } +} +export const UserMail = getModelForClass(UserMailClass, { existingConnection: UserMailClass.db })